Related
I have created the following c program based on a provided sample in order just to get messages from iot hub :
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>
#include "azure_c_shared_utility/shared_util_options.h"
#include "iothub_client.h"
#include "iothub_message.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/platform.h"
#include "iothubtransportmqtt.h"
#include "iothub_client_options.h"
#include "iothub_device_client_ll.h"
#include "iothub_device_client.h"
#include "iothub_client_sample_mqtt_esp8266.h"
/*String containing Hostname, Device Id & Device Key in the format: */
/* "HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>" */
/* "HostName=<host_name>;DeviceId=<device_id>;SharedAccessSignature=<device_sas_token>" */
static const char* connectionString = "HostName=mydevice";
static int callbackCounter;
//static char msgText[1024];
//static char propText[1024];
static bool g_continueRunning;
#define MESSAGE_COUNT 50
#define SET_TRUSTED_CERT_IN_SAMPLES
#define DOWORK_LOOP_NUM 3
#ifdef SET_TRUSTED_CERT_IN_SAMPLES
#include "certs.h"
#endif
typedef struct EVENT_INSTANCE_TAG
{
IOTHUB_MESSAGE_HANDLE messageHandle;
size_t messageTrackingId; // For tracking the messages within the user callback.
} EVENT_INSTANCE;
static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
int* counter = (int*)userContextCallback;
const char* buffer;
size_t size;
MAP_HANDLE mapProperties;
const char* messageId;
const char* correlationId;
// Message properties
if ((messageId = IoTHubMessage_GetMessageId(message)) == NULL)
{
messageId = "<null>";
}
if ((correlationId = IoTHubMessage_GetCorrelationId(message)) == NULL)
{
correlationId = "<null>";
}
// Message content
if (IoTHubMessage_GetByteArray(message, (const unsigned char**)&buffer, &size) != IOTHUB_MESSAGE_OK)
{
(void)printf("unable to retrieve the message data\r\n");
}
else
{
(void)printf("Received Message [%d]\r\n Message ID: %s\r\n Correlation ID: %s\r\n Data: <<<%.*s>>> & Size=%d\r\n", *counter, messageId, correlationId, (int)size, buffer, (int)size);
// If we receive the work 'quit' then we stop running
if (size == (strlen("quit") * sizeof(char)) && memcmp(buffer, "quit", size) == 0)
{
g_continueRunning = false;
}
}
// Retrieve properties from the message
mapProperties = IoTHubMessage_Properties(message);
if (mapProperties != NULL)
{
const char*const* keys;
const char*const* values;
size_t propertyCount = 0;
if (Map_GetInternals(mapProperties, &keys, &values, &propertyCount) == MAP_OK)
{
if (propertyCount > 0)
{
size_t index;
std::string uid;
printf(" Message Properties:\r\n");
for (index = 0; index < propertyCount; index++)
{
(void)printf("\tKey: %s Value: %s\r\n", keys[index], values[index]);
if (std::strcmp(keys[index],"uid") == 0 )
{
uid = values[index];
std::cout << "uid is " << uid << std::endl;
}
}
(void)printf("\r\n");
}
}
}
/* Some device specific action code goes here... */
(*counter)++;
return IOTHUBMESSAGE_ACCEPTED;
}
// static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
// {
// EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback;
// size_t id = eventInstance->messageTrackingId;
// (void)printf("Confirmation[%d] received for message tracking id = %d with result = %s\r\n", callbackCounter, (int)id, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result));
// /* Some device specific action code goes here... */
// callbackCounter++;
// IoTHubMessage_Destroy(eventInstance->messageHandle);
// }
static void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context)
{
(void)reason;
(void)user_context;
// This sample DOES NOT take into consideration network outages.
if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED)
{
(void)printf("The device client is connected to iothub\r\n");
}
else
{
(void)printf("The device client has been disconnected\r\n");
}
}
void iothub_client_sample_mqtt_esp8266_run(void)
{
IOTHUB_CLIENT_HANDLE iotHubClientHandle;
g_continueRunning = true;
srand((unsigned int)time(NULL));
callbackCounter = 0;
int receiveContext = 0;
if (platform_init() != 0)
{
(void)printf("Failed to initialize the platform.\r\n");
}
else
{
if ((iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, MQTT_Protocol)) == NULL)
{
(void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
bool traceOn = true;
IoTHubClient_SetOption(iotHubClientHandle, OPTION_LOG_TRACE, &traceOn);
#ifdef SET_TRUSTED_CERT_IN_SAMPLES
// For mbed add the certificate information
if (IoTHubClient_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)
{
(void)printf("failure to set option \"TrustedCerts\"\r\n");
}
#endif
(void)IoTHubDeviceClient_SetConnectionStatusCallback(iotHubClientHandle, connection_status_callback, NULL);
/* Setting Message call back, so we can receive Commands. */
if (IoTHubClient_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_LL_SetMessageCallback..........FAILED!\r\n");
}
else
{
(void)printf("IoTHubClient_LL_SetMessageCallback...successful.\r\n");
while (g_continueRunning)
{
ThreadAPI_Sleep(1000);
}
}
IoTHubClient_Destroy(iotHubClientHandle);
}
platform_deinit();
}
}
int main(void)
{
iothub_client_sample_mqtt_esp8266_run();
return 0;
}
Everything works fine. I get both messageid and correlationid correctly.
However I'd like to receive EventEnqueuedUtcTime at my callback which is mentioned here https://learn.microsoft.com/en-us/azure/stream-analytics/stream-analytics-define-inputs#configure-an-iot-hub-as-a-data-stream-input.
Since I'm quite new at this SDK I haven't a way to achieve this. Does anyone has an idea ?
I think you are confusing the device SDK and the service SDK. The EventEnqueuedUtcTime is available to a service side application that is reading the telemetry sent by devices to the IoT hub. I'm assuming that you expected to get that same value for a message delivered to your device via a Cloud to Device message which would be delivered to your callback function ReceiveMessageCallback. That property is not added to messages from the cloud to the device only messages from the device to the cloud.
I am trying to work with an API of one device, but it is using a WS interface with enforced Origin header, which is giving me troubles.
In Chrome, I can open the Console while a page with the correct Origin is loaded, create the WS connection, and send/receive messages without difficulties:
Note that sent messages (in green) are always acknowledged by the server.
For reference, this is what happens if I create the connection on a different page, which results in an Origin header mismatch, reported as 404:
To sidestep this problem, I turned to C, because the rest of my program is written in that anyway. This is the code I have right now, based mostly on this answer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <libwebsockets.h>
#define KGRN "\033[0;32;32m"
#define KCYN "\033[0;36m"
#define KRED "\033[0;32;31m"
#define KYEL "\033[1;33m"
#define KBLU "\033[0;32;34m"
#define KCYN_L "\033[1;36m"
#define KBRN "\033[0;33m"
#define RESET "\033[0m"
static int destroy_flag = 0;
static int connection_flag = 0;
static int writeable_flag = 0;
static void INT_HANDLER(int signo) {
destroy_flag = 1;
}
struct session_data {
int fd;
};
struct pthread_routine_tool {
struct lws_context *context;
struct lws *wsi;
};
static int websocket_write_back(struct lws *wsi_in, char *str, int str_size_in)
{
if (str == NULL || wsi_in == NULL)
return -1;
int n;
int len;
char *out = NULL;
if (str_size_in < 1)
len = strlen(str);
else
len = str_size_in;
out = (char *)malloc(sizeof(char)*(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING));
//* setup the buffer*/
memcpy (out + LWS_SEND_BUFFER_PRE_PADDING, str, len );
//* write out*/
n = lws_write(wsi_in, out + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT);
printf(KBLU"[websocket_write_back] %s\n"RESET, str);
//* free the buffer*/
free(out);
return n;
}
static int ws_service_callback(
struct lws *wsi,
enum lws_callback_reasons reason, void *user,
void *in, size_t len)
{
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED:
printf(KYEL"[Main Service] Connect with server success.\n"RESET);
connection_flag = 1;
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
printf(KRED"[Main Service] Connect with server error.\n"RESET);
destroy_flag = 1;
connection_flag = 0;
break;
case LWS_CALLBACK_CLOSED:
printf(KYEL"[Main Service] LWS_CALLBACK_CLOSED\n"RESET);
destroy_flag = 1;
connection_flag = 0;
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
printf(KCYN_L"[Main Service] Client recvived:%s\n"RESET, (char *)in);
if (writeable_flag)
destroy_flag = 1;
break;
case LWS_CALLBACK_CLIENT_WRITEABLE :
printf(KYEL"[Main Service] On writeable is called. send byebye message\n"RESET);
websocket_write_back(wsi, "{\"command\":\"subscribe\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"D0E91\\\"}\"}", -1);
websocket_write_back(wsi, "{\"command\":\"message\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"D0E91\\\"}\",\"data\":\"{\\\"value\\\":100,\\\"action\\\":\\\"set_buzz\\\"}\"}", -1);
writeable_flag = 1;
break;
default:
break;
}
return 0;
}
static void *pthread_routine(void *tool_in)
{
struct pthread_routine_tool *tool = tool_in;
printf(KBRN"[pthread_routine] Good day. This is pthread_routine.\n"RESET);
//* waiting for connection with server done.*/
while(!connection_flag)
usleep(1000*20);
//*Send greeting to server*/
lws_callback_on_writable(tool->wsi);
}
int main(void)
{
//* register the signal SIGINT handler */
struct sigaction act;
act.sa_handler = INT_HANDLER;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction( SIGINT, &act, 0);
struct lws_context *context = NULL;
struct lws_context_creation_info info;
struct lws *wsi = NULL;
struct lws_protocols protocol;
memset(&info, 0, sizeof info);
info.port = CONTEXT_PORT_NO_LISTEN;
info.iface = NULL;
info.protocols = &protocol;
info.ssl_cert_filepath = NULL;
info.ssl_private_key_filepath = NULL;
info.extensions = lws_get_internal_extensions();
info.gid = -1;
info.uid = -1;
info.options = 0;
protocol.name = "websockets";
protocol.callback = &ws_service_callback;
protocol.per_session_data_size = sizeof(struct session_data);
protocol.rx_buffer_size = 0;
protocol.id = 0;
protocol.user = NULL;
context = lws_create_context(&info);
printf(KRED"[Main] context created.\n"RESET);
if (context == NULL) {
printf(KRED"[Main] context is NULL.\n"RESET);
return -1;
}
wsi = lws_client_connect(context, "mobu1.herokuapp.com", 443, 1,
"/cable", "mobu1.herokuapp.com", "link.motorbunny.com",
if (wsi == NULL) {
printf(KRED"[Main] wsi create error.\n"RESET);
return -1;
}
printf(KGRN"[Main] wsi create success.\n"RESET);
struct pthread_routine_tool tool;
tool.wsi = wsi;
tool.context = context;
pthread_t pid;
pthread_create(&pid, NULL, pthread_routine, &tool);
pthread_detach(pid);
while(!destroy_flag)
{
lws_service(context, 50);
}
lws_context_destroy(context);
return 0;
}
The result of running the above program is this:
As you can see, the periodic pings from server to my client are being picked up, but the lws_callback_on_writable(wsi); seems to have no effect as the LWS_CALLBACK_CLIENT_WRITEABLE callback never gets called. Additionally, if I call websocket_write_back() directly anywhere else, it doesn't seem to be sending anything to the server, and no acknowledgement is present either.
Is there something obvious I am doing wrong?
EDIT 1:
I found this neat wscat, where I can replicate the results from Chrome:
Now the question is, how can I interface this with my C program in a way that it can wait for the Welcome message from the server, and then send two messages?
And better yet, how to stay connected, so that my program can send multiple commands at different points of time without having to do the handshake all the time?
The reason why the LWS_CALLBACK_CLIENT_WRITEABLE callback never got called was because this particular server uses non-standard handshake. So, to bypass this, I forked a fork of libwsclient and modified the handshake checking function to not fail on mismatch. I also added an optional Origin header.
Now, all I need to do in my original program is
wsclient *client;
char sync_str[6];
void mb_send(int power, char* type)
{
char cmd[2048];
sprintf (cmd, "{\"command\":\"message\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"%s\\\"}\",\"data\":\"{\\\"value\\\":%d,\\\"action\\\":\\\"set_%s\\\"}\"}",sync_str,power,type);
libwsclient_send(client,cmd);
}
void mb_connect()
{
char cmd[2048];
sprintf (cmd, "{\"command\":\"subscribe\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"%s\\\"}\"}",sync_str);
libwsclient_send(client,cmd);
mb_send(0,"buzz");
}
int nop()
{
return 0;
}
int main()
{
client = libwsclient_new_extra("wss://mobu1.herokuapp.com/cable","https://link.motorbunny.com");
if(!client) {
fprintf(stderr, "Unable to initialize new WS client.\n");
exit(1);
}
libwsclient_onopen(client, &nop);
libwsclient_onmessage(client, &nop);
libwsclient_onerror(client, &nop);
libwsclient_onclose(client, &nop);
libwsclient_run(client);
...
mb_connect();
...
mb_send(200,"buzz");
mb_send(40,"twirl");
...
mb_send(0,"buzz");
mb_send(0,"twirl");
}
I found an ugly hack to make my C program send WebSocket messages to a server via the wsta program.
It requires a text file, into which my program will append whenever it wants to send a message to the server. The new lines are then picked up in the background by tail -f, and are piped to wsta which maintains the connection. Output can be redirected to /dev/null so that the wsta output doesn't pollute the output of my program, or sent to a file if responses from the server need to be parsed.
The whole script to make this work would look like this (or you could use FIFO pipe with cat instead of a file with tail):
#!/bin/bash
touch commands.txt
tail commands.txt -f -n 0 | wsta --header "Origin: https://link.motorbunny.com" "wss://mobu1.herokuapp.com/cable" &> /dev/null &
./program
In the C program, I just need to write to the commands.txt file:
FILE* cmd;
char sync_str[6];
void mb_connect()
{
fprintf (cmd, "{\"command\":\"subscribe\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"%s\\\"}\"}\n",sync_str);
fflush(cmd);
}
void mb_send(int power, char* type)
{
fprintf (cmd, "{\"command\":\"message\",\"identifier\":\"{\\\"channel\\\":\\\"DevicesChannel\\\",\\\"share_token\\\":\\\"%s\\\"}\",\"data\":\"{\\\"value\\\":%d,\\\"action\\\":\\\"set_%s\\\"}\"}\n",sync_str,power,type);
fflush(cmd);
}
int main()
{
cmd = fopen ("commands.txt","w");
...
mb_connect();
...
mb_send(200,"buzz");
...
mb_send(0,"buzz");
}
I'm using libusb to communicate with a Philips ISP1362 configured as a USB device. I am able to successfully loopback data using Synchronous I/O without any problems. For some reason when using Asynchronous I/O there appears to be a race condition.
I am transferring 64-byte packets using back-to-back OUT-IN transfers. Occasionally when I run my program libusb throws a timeout error and some of the loopback data is lost. When analyzing the USB bus using my Beagle 12 I can see the OUT-IN transactions are out of order (i.e. OUT-OUT-IN-TIMEOUT) when it should be (OUT-IN-OUT-IN).
Update The transfers are appearing out of order in the callback function which is strange because they are not coinciding with what is actually on the bus analyzer.
Example 1: (IN-OUT-IN-OUT)
main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed
Example 2: (OUT-IN-IN-OUT)
main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed
Below is a screenshot from the analyzer:
Below is the code:
#include <stdlib.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 4 // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 3*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;
/* use a global variable to keep the context */
static struct libusb_context *usb_context = NULL;
/* count variable */
int count = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
void xfr_cb(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
/* callback - This is called after the transfer has been received by libusb */
fprintf(stderr, "xfr_cb(): count %d, status = %d, endpoint = %x, actual_length = %d, completed = %d\n",
count,
transfer->status,
transfer->endpoint,
transfer->actual_length,
*completed);
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
{
/* Error! */
fprintf(stderr, "Error: %s\n", libusb_error_name((int)transfer->status));
}
if (count == TRANSFERS-1)
*completed = 1;
count++;
}
int main(int argc, char **argv)
{
int ep_addr;
int completed = 0;
unsigned char *buf;
size_t length = 64;
int n;
int i;
int rc;
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!devh)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* allocate memory */
buf = malloc(length);
/* start with OUT transfer */
ep_addr = ep_out;
/* queue up alternating OUT-IN transfers */
for (i = 0; i < TRANSFERS; i++)
{
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE; n++)
{
buf[n] = i+n;
}
/* Set up the transfer object */
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, devh, ep_addr, buf, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
/* Submit the transfer object */
libusb_submit_transfer(transfer);
fprintf(stderr, "main(): submitting transfer %d, endpoint %x\n", i, ep_addr);
/* alternate writing and reading for loopback */
ep_addr = (ep_addr == ep_out) ? ep_in : ep_out;
}
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc < 0)
{
if (rc == LIBUSB_ERROR_INTERRUPTED)
continue;
fprintf(stderr, "Transfer Error: %s", libusb_error_name(rc));
continue;
}
}
fprintf(stderr, "completed\n");
/* Release the interface */
libusb_release_interface(devh, 0);
/* Close the device handle */
if (devh)
libusb_close(devh);
out:
if (devh)
{
libusb_close(devh);
}
libusb_exit(NULL);
return rc;
}
Update 2 I successfully eliminated the timeout. The cause of the libusb timeout is because the Host was sending two consecutive OUT transactions intermittently on the bus.
Analyzer screenshot:
The following is the working code (no timeouts). Ran these thousands of times with no issues
static void LIBUSB_CALL xfr_cb(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
unsigned char *wbuf, *rbuf;
size_t length = 64;
fprintf(stderr, "xfr_cb(): status = %d, endpoint = %x, actual_length = %d\n",
transfer->status,
transfer->endpoint,
transfer->actual_length);
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int ep_addr;
int completed = 0;
unsigned char *buf, *wbuf1, *wbuf2, *rbuf1, *rbuf2;
size_t length = 64;
int n;
int m;
int i;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
wbuf1 = malloc(length);
wbuf2 = malloc(length);
rbuf1 = malloc(length);
rbuf2 = malloc(length);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE; n++)
wbuf1[n] = n;
for (m = 0; m < EP_SIZE; m++)
wbuf2[m] = m+1;
struct libusb_transfer *transfer1;
struct libusb_transfer *transfer2;
struct libusb_transfer *transfer3;
struct libusb_transfer *transfer4;
/* Set up the transfer object */
transfer1 = libusb_alloc_transfer(0);
transfer2 = libusb_alloc_transfer(0);
transfer3 = libusb_alloc_transfer(0);
transfer4 = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, xfr_cb, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
/* Submit the transfers */
libusb_submit_transfer(transfer1);
libusb_submit_transfer(transfer2);
libusb_submit_transfer(transfer3);
libusb_submit_transfer(transfer4);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Changing the code as follows (i.e. callback = NULL for transfer 1-3) re-creates intermittent duplicate transactions, as shown in the screenshots.
libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, NULL, NULL, TIMEOUT);
libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */
I honestly don't understand why the loop would cause race conditions based on their documentation and examples. Queueing up multiple transfers is actually suggested in one of the libusb examples (sam3u_benchmark.c) and also demonstrated (using loops) in the following .pdfs.
See asynchronous I/O sections:
https://www.socallinuxexpo.org/sites/default/files/presentations/scale_2017_usb.pdf
http://www.signal11.us/oss/elc2014/elc_2014_usb_0.pdf
From my understanding, the use of libusb_handle_events_completed(NULL, &completed) is supposed to resolve synchronization issues. Am I misunderstanding something?
See libusb_handle_events() from multiple threads
http://libusb.sourceforge.net/api-1.0/libusb_mtasync.html
-"This is why libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed() and libusb_handle_events_completed() functions, which handles doing the completion check for you after they have acquired the lock:"
What they need are crystal clear examples of how to use their API if this is the case.
I can add more event checking but something does not seem right here.
Update 3: See accepted answer.
I started reading the documentation in the libusb source code and understood what was happening.
Particularly the section about how libusb deals with packet sizes:
http://libusb.sourceforge.net/api-1.0/libusb_packetoverflow.html
After reading that it clicked for me and I found two ways to accomplish a loopback test with large data size using asynchronous I/O.
The first way is submitting two transfers consecutively with transfer->buffer containing the entire data structure (i.e. total bytes to send and receive). The second way is submitting the two transfers with transfer->buffer containing wMaxPacketSize (e.g. 64-bytes) and having the out and in callback functions submit additional transfers to transceive the rest of the data.
For the second case, extra code needed to be added to keep track of the number of transfers and to set the completed signal when finished. The OUT-IN packet interleaving is handled by libusb and the OS - which was the part I didn't realize. In other words, not every OUT-IN transfer needed to be specified and queued individually.
Here is the asynchronous code along with the transfer rates to my USB device (ISP1362). My USB device controller is an FPGA coded in pure SystemVerilog.
Note: Regarding the transfer rates, I only have double-buffering enabled on BULK_EP_IN. I am assuming the IN-NAK's (# POLL) and transfer rate would improve in the second approach if double-buffering was enabled on BULK_EP_OUT. So this may not be a fair comparison due to device configuration.
First approach: ~1.161 MB/s (~9.288 Mb/s)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define TIMEOUT 10*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf_tmp[EP_SIZE*TRANSFERS];
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
}
static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);
count_in++; // one transfer complete
if (count_in < TRANSFERS)
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int completed = 0;
size_t length = 64;
int n;
int m;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* fill the buffer with incrementing data */
for (n = 0; n < TRANSFERS; n++)
{
for (m = 0; m < EP_SIZE; m++)
{
wbuf_tmp[m+n*EP_SIZE] = m+n;
}
}
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_in, &completed, TIMEOUT);
libusb_submit_transfer(transfer);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
else
fprintf(stderr, "success\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Second approach: ~755.9 MB/s (~6.047 Mb/s)
include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define TIMEOUT 10*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char *wbuf_tmp;
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
count_out++; // one transfer complete
if (count_out < TRANSFERS)
{
transfer->buffer = ++wbuf_tmp;
libusb_submit_transfer(transfer);
}
}
static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
int *completed = transfer->user_data;
memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);
count_in++; // one transfer complete
if (count_in < TRANSFERS)
libusb_submit_transfer(transfer);
else
*completed = 1;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
int completed = 0;
size_t length = 64;
int n;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
wbuf_tmp = malloc(length*TRANSFERS);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE*TRANSFERS; n++)
{
wbuf_tmp[n] = n;
}
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE, xfr_cb_in, &completed, TIMEOUT);
libusb_submit_transfer(transfer);
/* Handle Events */
while (!completed)
{
rc = libusb_handle_events_completed(NULL, &completed);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
else
fprintf(stderr, "success\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
Update: See accepted answer.
The following is an example using Synchronous I/O. I had a lot of trouble getting the transactions to come out in the expected order using Asynchronous I/O. I assume this was due to transfers racing with each other as #Gene had mentioned.
The main gripe I have about the libusb API is the lack of examples to illustrate proper use. The API would lead someone to believe that asynchronous transactions are placed on the bus in the order they are "submitted" and from what I gather this is not true. This functionality would be fine for submitting transactions with all the same packet TOKEN (i.e. OUT or IN).
The following code works for large bulk transfers.
Using Synchronous I/O
#include <stdlib.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
/* Change VENDOR_ID and PRODUCT_ID depending on device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define BYTES 1024*768*3 // bytes
#define EP_SIZE 64 // bytes
#define TIMEOUT 5*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in_addr = 0x82;
static int ep_out_addr = 0x01;
int write_chars(unsigned char * data, int length)
{
/* To send a char to the device simply initiate a bulk_transfer to the Endpoint
* with the address ep_out_addr.
*/
int actual_length;
int rc = libusb_bulk_transfer(devh, ep_out_addr, data, length, &actual_length, TIMEOUT);
if (rc < 0)
{
fprintf(stderr, "Error while sending char: %d\n", rc);
return -1;
}
return actual_length;
}
int read_chars(unsigned char * data, int length)
{
/* To receive characters from the device initiate a bulk_transfer to the Entpoint
* with address ep_in_addr
*/
int actual_length;
int rc = libusb_bulk_transfer(devh, ep_in_addr, data, length, &actual_length, TIMEOUT);
if (rc == LIBUSB_ERROR_TIMEOUT)
{
printf("timeout (%d)\n", actual_length);
return -1;
}
else if (rc < 0)
{
fprintf(stderr, "Error while waiting for char: %d\n", rc);
return -1;
}
return actual_length;
}
int main(int argc, char **argv)
{
int rc;
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!devh)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* We can now start sending or receiving data to the device */
unsigned char buf[BYTES];
unsigned char rbuf[EP_SIZE];
int len;
int n;
int l;
int res;
// fill buffer
for (n = 0; n < BYTES; n++)
{
buf[n] = 0x00+n;
}
// loopback data, write-read
for (l = 0; l < BYTES/EP_SIZE; l++)
{
len = write_chars(buf+l*EP_SIZE, EP_SIZE);
len = read_chars(rbuf, EP_SIZE);
res = memcmp(rbuf, buf+l*EP_SIZE, sizeof(rbuf));
if (res != 0)
fprintf(stderr, "Miscompare: block %d\n", l);
}
libusb_release_interface(devh, 0);
out:
if (devh)
{
libusb_close(devh);
}
libusb_exit(NULL);
return rc;
}
Using Asynchronous and Synchronous together (i.e. OUT is submitted Asynchronously and IN is Synchronous)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include </usr/include/libusb-1.0/libusb.h>
/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID 0x0471
#define PRODUCT_ID 0x3630
/* Define number of bytes to transfer */
#define EP_SIZE 64 // bytes
#define TRANSFERS 1024*768*3/EP_SIZE // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 15*1000 // milliseconds
/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;
/* count variable */
unsigned int count_out = 0;
/* The Endpoint addresses are hard-coded. You should use libusb -v to find
* the values corresponding to device
*/
static int ep_in = 0x82;
static int ep_out = 0x01;
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf[EP_SIZE*TRANSFERS];
static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
count_out++;
}
int main(int argc, char **argv)
{
const struct libusb_version *version;
unsigned char *buf, *rbuf_tmp;
size_t length = 64;
int n;
int i;
int rc;
/* Get libusb version */
version = libusb_get_version();
fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);
/* Initialize libusb */
rc = libusb_init(NULL);
if (rc < 0)
{
fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level */
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it */
handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!handle)
{
fprintf(stderr, "Error finding USB device\n");
goto out;
}
/* claim interface */
rc = libusb_claim_interface(handle, 0);
if (rc < 0)
{
fprintf(stderr, "Error claiming interface.\n");
goto out;
}
/* allocate memory */
buf = malloc(length*TRANSFERS);
/* fill the buffer with incrementing data */
for (n = 0; n < EP_SIZE*TRANSFERS; n++)
{
buf[n] = n;
}
/* allocate memory */
rbuf_tmp = malloc(length);
/* set up alternating OUT-IN transfers */
for (i = 0; i < TRANSFERS; i++)
{
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, ep_out, buf+i, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
libusb_submit_transfer(transfer);
int actual_length;
int rc = libusb_bulk_transfer(handle, ep_in, rbuf_tmp, EP_SIZE, &actual_length, TIMEOUT);
if (rc != LIBUSB_SUCCESS)
{
fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
break;
}
memcpy(rbuf+i*EP_SIZE, rbuf_tmp, EP_SIZE);
}
fprintf(stderr, "completed\n");
int res;
res = memcmp(rbuf, wbuf, sizeof(wbuf));
if (res != 0)
fprintf(stderr, "miscompare\n");
//* Release the interface */
libusb_release_interface(handle, 0);
/* Close the device handle */
if (handle)
libusb_close(handle);
out:
if (handle)
{
libusb_close(handle);
}
libusb_exit(NULL);
return rc;
}
The above code was an experiment to see if performance increased. Interestingly, the speed difference between the two was negligible.
The version of libusb was 1.0.17.10830
I am trying to use OpenSSL in C to make an https request.
My code runs just fine in OSX and Linux. It has serious issues in Windows.
I am using the example code in the download found here https://wiki.openssl.org/index.php/SSL/TLS_Client (at bottom of page)
I can get one good run out of it (maybe), all subsequent attempts fail.
When running debug in visual studio it will fail on the SSL_CTX_load_verify_locations line. Visual Studio simply tells me there is a corrupted heap. I've checked file permissions, I've checked paths, checking variables when on the exception debug it looks like all the variables are fine, but it doesn't seem to get past the verify. I'm using LibreSSL SDK.
const char *file_loc = "C:\\Users\\{USER}\\Desktop\\trusted-roots.pem";
res = SSL_CTX_load_verify_locations(ctx, file_loc, NULL);
I have noticed that if I can get it to pass, on those rare events, instead of fully letting it finish, just stopping the debug, I can run it to that same point again without problem.
I have tried using a different pem file to make sure it wasn't just the one being locked, but that doesn't make a difference.
I'm happy to debug more, but I'd need some pointers on how to get more information from VS.
The relevant snippet of code below:
int openssl_fetch(char *request, HttpResponse *response)
{
char *HOST_NAME = "abc.com"; // Substituted for privacy
char *HOST_PORT = "443";
long res = 1;
int ret = 1;
unsigned long ssl_err = 0;
SSL_CTX* ctx = NULL;
BIO *web = NULL, *out = NULL;
SSL *ssl = NULL;
do {
/* Internal function that wraps the OpenSSL init's */
/* Cannot fail because no OpenSSL function fails ??? */
init_openssl_library();
/* https://www.openssl.org/docs/ssl/SSL_CTX_new.html */
const SSL_METHOD* method = SSLv23_method();
ssl_err = ERR_get_error();
//ASSERT(NULL != method);
if (!(NULL != method))
{
print_error_string(ssl_err, "SSLv23_method");
break; /* failed */
}
/* http://www.openssl.org/docs/ssl/ctx_new.html */
ctx = SSL_CTX_new(method);
/* ctx = SSL_CTX_new(TLSv1_method()); */
ssl_err = ERR_get_error();
//ASSERT(ctx != NULL);
if (!(ctx != NULL))
{
print_error_string(ssl_err, "SSL_CTX_new");
break; /* failed */
}
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
/* Cannot fail ??? */
/* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
SSL_CTX_set_verify_depth(ctx, 5);
/* Cannot fail ??? */
/* Remove the most egregious. Because SSLv2 and SSLv3 have been */
/* removed, a TLSv1.0 handshake is used. The client accepts TLSv1.0 */
/* and above. An added benefit of TLS 1.0 and above are TLS */
/* extensions like Server Name Indicatior (SNI). */
const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
long old_opts = SSL_CTX_set_options(ctx, flags);
UNUSED(old_opts);
/* http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html */
// res = SSL_CTX_load_verify_locations(ctx, "random-org-chain.pem", NULL);
const char *file_loc = "C:\\Users\\jshackelford\\Desktop\\trusted-roots.pem";
//const char *file_loc = "/C/Users/jshackelford/Desktop/trusted-roots.pem"; //Tried both
res = SSL_CTX_load_verify_locations(ctx, file_loc, NULL);
ssl_err = ERR_get_error();
}
return 0;
}
I want to send message from client A to client B, through this server. I am not sure how can I get that? One approach I can think of is making a message queue for each client and add message to that queue if someone sends message to that client, and sends from that queue to the respective client ? But I am not sure How can I implement this ? Can anyone help me with this ?
There can be n clients at any moments. In that case broadcast to all clients.
Below is my server code. I have checked username and password of the user. insidePortal() function will take care of sending message to other client.
#include<stdio.h>
#include <stdlib.h>
#include<string.h> //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h> //write
int main(int argc , char *argv[])
{
int socket_desc , client_sock , c , read_size, pid;
struct sockaddr_in server , client;
char client_message[2000], message_sent[2000], message_recieve[2000];
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 5550 );
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
while (1) {
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
// accept connection from an incoming client
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
/* Create child process */
pid = fork();
if (pid == 0) {
/* This is the client process */
close(socket_desc);
puts("Connection accepted");
char username[50], password[50];
memset(message_recieve, 0, sizeof message_recieve);
recv(client_sock , username , 2000 , 0);
printf("username of the user: %s", username);
memset(message_recieve, 0, sizeof message_recieve);
recv(client_sock , password , 2000 , 0);
printf("password of the user: %s", password);
FILE *f = fopen("registeredUsers.txt", "r");
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
char uname[50], pass[50];
int login = 0;
while(fscanf(f, "%s %s\n", uname, pass) > 0) {
printf("Helllo %s %s", uname, pass);
if (strcmp(username, uname)==0 && strcmp(password, pass) == 0) {
login = 1;
break;
}
}
memset(message_sent, 0, sizeof message_sent);
if (login == 1) {
strcpy(message_sent,"\n Successfull Login\n");
write(client_sock , message_sent , strlen(message_sent)); // Sends login status
insidePortal(client_sock, username);
} else {
strcpy(message_sent,"\nOops, wrong username or password. Please try again.\n");
write(client_sock , message_sent , strlen(message_sent)); // Sends login status
}
fclose(f);
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
exit(0);
} else {
close(client_sock);
}
}
return 0;
}
void insidePortal(int client_sock, char username[50]) {
}
I have this really long code I've wrote in the past that implements something similar to yours, but It's CPP, If you want too I'll post it here, but basically using message queues or multi-process programming seems kinda useless to me, If I were you I'd program it in the following way
Client code -> Two Threads
Server code -> Two Threads
Client Has two threads, and Three functions, Connect / Send / Receive
Connect - This function handles connection to server, whether you are using TCP it handles the listen-accept or if you use some UDP based made up protocol, it just handles it - makes sure you have some connectin
Send - This function sends some data to server
Receive This functino receives data from server
The flow of Client would be the following:
Client connects to server on Main thread
After connecting to server Client creates Second thread
On Main thread - Client enters some loop that reads data from user as Input then calls Send function and sends it to server
On Second thread - Client enters some loop that calls Receive
to receive data from server and prints it when data is received
that handles Client, now about Server
Server- Has Three functions and Some one Global data structure called Linked - List ( a Linked list obviously ) that would be shared by all it's threads, WaitForConnection Receive SendToAll
WaitForConnection- Simply calls the "Accept" function of sockets API ( if you're using TCP ) or if you're using some other made up protocol, this function just blocks it's thread as trying to wait for incoming connection, when some connection arrives, this function registers the connection into the global linked-list of all connections called Connection-List, with the appropriate socket and client data
SendToAll - Simply iterates all Connection-List and for every Connection within that list, It sends some data passed
Receive just receives some data but sets some timeout first, this is very important in order for Receive not to block for too long!
The flow of Server would be the following:
Main thread creats Second thread
Main thread enters some loop and calls WaitForConnection within it in order to continuously get connections and add them into Connection-List
Second thread enters some loop that iterates over Connection-List, for each connection within Connection-List, receive is called upon the appropriate socket, if some data is receive, SendToAll is called with the data received, if timeout, nothing happens, after that loop is continued and the next loop iteration is executed
SendToAll sends the data to ALL clients within Connection-List
This is some very simple Multi-Client Broadcast with Server architecture It should be really easy to implement! I hope this helps you
I Apologize in advance as this is a code I wrote a while ago so It has a lot of nesting within it, and a lot of comments!
------------------------- CLIENT CODE ----------------
// Main.cpp
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#pragma comment (lib, "Ws2_32.lib")
#define NAME_LENGTH 40
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8888"
void Receive(SOCKET* pscktConnection);
void Send(SOCKET* pscktConnection);
void main()
{
// Variable definition
int nResult;
int nNameLength = 0;
char pcNameBuffer[NAME_LENGTH];
SOCKET sckConnection = NULL;
WSADATA wsaData;
addrinfo addrAddressFormat;
addrinfo* paddrServerAddress;
// Code section
// Initialize Winsock
nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// If winsock dll loading has failed
if (nResult != 0)
{
std::cout << "Failed loading winsock DLL" << std::endl;
}
// DLL loaded successfully
else
{
//Setup connection info
ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
addrAddressFormat.ai_family = AF_INET;
addrAddressFormat.ai_socktype = SOCK_STREAM;
addrAddressFormat.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port with the address setting into our final address
nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrServerAddress);
// Address resolving has failed
if (nResult != 0)
{
std::cout << "Some error has occured during connection establishment" << std::endl;
}
else
{
// Request user for his name
pcNameBuffer[0] = '\0';
std::cout << "PLEASE ENTER YOUR NAME -> ";
std::cin.getline(pcNameBuffer, NAME_LENGTH);
std::cout << std::endl << std::endl ;
// Creating the socket
sckConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Connecting
nResult = connect(sckConnection, paddrServerAddress->ai_addr, (int)paddrServerAddress->ai_addrlen);
// Creating of the socket has failed
if (nResult == SOCKET_ERROR)
{
std::cout << "Creating of the socket has failed" << std::endl;
}
// Send server user's name
else
{
// Measure the name length
while (pcNameBuffer[nNameLength] != '\0')
{
++nNameLength;
}
// If invalid name
if (nNameLength == 0)
{
pcNameBuffer[0] = 'G';
pcNameBuffer[1] = 'U';
pcNameBuffer[2] = 'E';
pcNameBuffer[3] = 'S';
pcNameBuffer[4] = 'T';
pcNameBuffer[5] = '\0';
nNameLength = 6;
}
nResult = send(sckConnection, pcNameBuffer, nNameLength + 1, 0);
// An error has occured while sending server the user's name
if (nResult <= 0)
{
std::cout << "Some error has occured" << std::endl;
}
// Good to go
else
{
std::thread Read(Receive, &sckConnection);
Send(&sckConnection);
}
}
}
}
// cleanup resources
WSACleanup();
}
/*
* [Description]: This method is used only to read messages from server and print them
* [Paramaters]:
* pscktServerSocket - The address of the our connection socket
* [Return Value]: none
*/
void Receive(SOCKET* pscktConnection)
{
// Variable definition
int nReceivedBytes;
int nBufferLen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN];
// Code section
// Keep this operation running constantly
while (true)
{
// Read from server -- NO TIME OUT NEEDED
nReceivedBytes = recv((*pscktConnection), pcBuffer, nBufferLen, 0);
// More than zero bytes received
if (nReceivedBytes > 0)
{
// Set a zero termination to simulate a string
pcBuffer[nReceivedBytes] = '\0';
std::cout << pcBuffer << std::endl;
}
// Server has closed the connection probably
else
{
// TODO - CLOSE CONNECTION
}
}
}
/*
* [Description]: This method is used only to send messages to the server
* [Paramaters]:
* pscktServerSocket - The address of the our connection socket
* [Return Value]: none
*/
void Send(SOCKET* pscktConnection)
{
// Variable definition
int nSentBytes;
int nBufferLen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN];
// Code section
pcBuffer[0] = '\0';
// Keep this operation running constantly
while (true)
{
int nSentBytes = 0;
// Read
std::cin.getline(pcBuffer, nBufferLen);
// Go through string untill backslash 0
while (pcBuffer[nSentBytes] != '\0')
{
// Increase the number of bytes we want to send
++nSentBytes;
}
pcBuffer[nSentBytes] = '\0';
nSentBytes = send((*pscktConnection), pcBuffer, nSentBytes, 0);
// An error has occured
if (nSentBytes == SOCKET_ERROR)
{
// TODO - HANDLE ERROR;
}
}
}
`
------------------------- SERVER CODE ----------------
// Source.cpp
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include "Client.h"
#include "Connections.h"
#pragma comment (lib, "Ws2_32.lib")
#define NAME_LENGTH 40
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8888"
#define MAX_CONNECTIONS 5
// Globals
Connections* conAllConnections = Connections::getInstance();
bool LoadServerSocket(SOCKET* pscktServerSocket);
void Dispatcher(SOCKET* pscktServerSocket);
void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength);
void HandleConnections();
void main()
{
// Variable definition
int nResult;
SOCKET sckServerSocket = NULL;
WSADATA wsaData;
// Code section
// Initialize Winsock
nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// If winsock dll loading has failed
if (nResult != 0)
{
std::cout << "Failed loading winsock DLL" << std::endl;
}
// DLL loaded successfully
else
{
// If failed loading the server socket
if (!LoadServerSocket(&sckServerSocket))
{
std::cout << "Failed loading the server socket!" << std::endl;
}
else
{
std::thread dispatch(Dispatcher,&sckServerSocket);
//dispatch.join();
HandleConnections();
}
}
// cleanup resources
WSACleanup();
}
/*
* [Description]: This method is used to load and bind server socket into some pointer.
* [Paramaters]:
* pscktServerSocket - a pointer variable that we would like to load our socket into the address this pointer
* is pointing at
* [Return Value]: A boolean indication of whether our socket was created successfully
*/
bool LoadServerSocket(SOCKET* pscktServerSocket)
{
// Variable definition
int nResult;
bool bWasServerSocketCreated = false;
bool bWasAddressResolved = false;
addrinfo addrAddressFormat;
addrinfo* paddrFinalAddress = NULL;
// Code section
// Fil addrAddressFormat with zeros, and set correct settings of our desired socket
ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
addrAddressFormat.ai_family = AF_INET;
addrAddressFormat.ai_socktype = SOCK_STREAM;
addrAddressFormat.ai_protocol = IPPROTO_TCP;
//addrAddressFormat.ai_flags = AI_PASSIVE;
// Resolve the server address and port with the address setting into our final address
nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrFinalAddress);
// If resolving of the address was successful
if (nResult == 0)
{
// Set address resolving bool indication to true
bWasAddressResolved = true;
// Create server socket
(*pscktServerSocket) = socket(paddrFinalAddress->ai_family,
paddrFinalAddress->ai_socktype,
paddrFinalAddress->ai_protocol);
// Socket creating was successful
if ((*pscktServerSocket) != INVALID_SOCKET)
{
// Set socket creation indication to true
bWasServerSocketCreated = true;
// Bind our socket into our address
nResult = bind((*pscktServerSocket),
paddrFinalAddress->ai_addr,
(int)paddrFinalAddress->ai_addrlen);
// In case binding failed
if (nResult == SOCKET_ERROR)
{
closesocket((*pscktServerSocket));
bWasServerSocketCreated = false;
}
}
}
// Freeing resources
if (bWasAddressResolved)
{
freeaddrinfo(paddrFinalAddress);
}
return (bWasServerSocketCreated);
}
/*
* [Description]: This uses the loaded server socket and handles incoming requests for connections
* [Paramaters]:
* pscktServerSocket - a pointer to the loaded server socket
* [Return Value]: none
*/
void Dispatcher(SOCKET* pscktServerSocket)
{
// Variable definition
int nResult;
char pcBuffer[NAME_LENGTH];
DWORD timeout = 1500;
SOCKET sckClientSocket;
Client clntNewClient;
// Code section
// Keep this running constantly
while (true)
{
// Keep this running as long as we have the sufficient amount of connections
while (conAllConnections->getNumOfConnections() < MAX_CONNECTIONS)
{
// Attempt listening on the server socket
nResult = listen((*pscktServerSocket), MAX_CONNECTIONS);
// Listening was a failure
if (nResult == SOCKET_ERROR)
{
std::cout << "Failed listening with the server socket" << std::endl;
// HANDLE ERROR - TODO
}
// Listening was successful
else
{
std::cout << "Listening...." << std::endl;
// Accept a client socket
sckClientSocket = accept((*pscktServerSocket), NULL, NULL);
// Accepting was a failure
if (sckClientSocket == INVALID_SOCKET)
{
std::cout << "Client accepting has failed" << std::endl;
// HANDLE ERROR - TODO
}
// Client was added successfully
else
{
setsockopt(sckClientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
nResult = recv(sckClientSocket, pcBuffer, NAME_LENGTH, 0);
// If received a valid username
if (nResult > 0)
{
timeout = 1;
std::cout << "New Client -> " << pcBuffer << std::endl;
clntNewClient.setClientSocket(sckClientSocket);
clntNewClient.setIsAdmin(false);
clntNewClient.setClientName(pcBuffer);
setsockopt(clntNewClient.getClientSocket(), SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
conAllConnections->Add(clntNewClient);
// Receive until the peer shuts down the connection
}
}
}
}
}
}
/*
* [Description]: This method forwards a message to all other clients but the client who sent it
* [Paramaters]:
* pclndSenderAddress - The address of the client node who sent the
* pcMessageBuffer- a pointer to the message buffer
* nLength - the length of the message
* [Return Value]: none
*/
void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength)
{
// Variable definition
int nError;
int nResult;
Client clntCurrentClient;
ClientNode* pclndCurrentNode;
ClientNode* pclndNextNode;
// Code section
// Set first node
pclndCurrentNode = conAllConnections->getFirst();
// Go through all connections
while (pclndCurrentNode != NULL)
{
// Save the next node in this phase of the code in order to avoid corruption of memory
// in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
pclndNextNode = pclndCurrentNode->getNext();
// Compare addresses, we do not want to forward the message to the sender
if (pclndCurrentNode != pclndSenderAddress)
{
clntCurrentClient = pclndCurrentNode->getClient();
// Forward the message
nResult = send(clntCurrentClient.getClientSocket(), pcMessageBuffer, nLength, 0);
// An error has occured
if (nResult == SOCKET_ERROR)
{
nError = WSAGetLastError();
// TODO -- handle later
}
}
// Forward current node
pclndCurrentNode = pclndNextNode;
}
}
/*
* [Description]: This method handles and receives messages from our clients and forwards them
* [Paramaters]: none
* [Return Value]: none
*/
void HandleConnections()
{
// Variable definition
int nIndex;
int nError;
int nRecvLen;
int nNameLen;
int nRecvbuflen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN + NAME_LENGTH + 3];
Client clntCurrentClient;
ClientNode* pclndCurrentNode;
ClientNode* pclndNextNode;
// Code section
// Keep this going constantly
while (true)
{
pclndCurrentNode = conAllConnections->getFirst();
// Go through all connections
while (pclndCurrentNode != NULL)
{
clntCurrentClient = pclndCurrentNode->getClient();
// Save the next node in this phase of the code in order to avoid corruption of memory
// in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
pclndNextNode = pclndCurrentNode->getNext();
// Attempt receiving data from client
nRecvLen = recv(clntCurrentClient.getClientSocket(), pcBuffer, nRecvbuflen, 0);
// An error has occured
if (nRecvLen <= 0)
{
nError = WSAGetLastError();
// if not a timeout error
if (nError != 10060)
{
std::cout << "Client removed" << std::endl;
// Socket error, remove connection
conAllConnections->Remove(pclndCurrentNode);
}
}
// No error has occured
else
{
//// The purpose of this part of the code is only to place a [CLIENTNAME]
//// prefix within the begining of each message
////--------------------------------////
// Get client's name length
nNameLen = clntCurrentClient.getNameLength();
nIndex = nRecvLen - 1;
// Copy the message some offset forward -- offset is (namn length + 4)
while (nIndex >= 0)
{
// Copy letter (namelen + 4) times forward
pcBuffer[nIndex + nNameLen + 4] = pcBuffer[nIndex];
// Reduce nIndex
--nIndex;
}
pcBuffer[0] = '[';
nIndex = 0;
// Place clients name within message
while (nIndex < nNameLen)
{
// + 1 for offset
pcBuffer[nIndex + 1] = (clntCurrentClient.getClientName())[nIndex];
// Increase nIndex
++nIndex;
}
pcBuffer[nIndex + 1] = ']';
pcBuffer[nIndex + 2] = ':';
pcBuffer[nIndex + 3] = ' ';
////--------------------------------////
//// No longer adding a prefix code
SendAll(pclndCurrentNode, pcBuffer, nRecvLen + nNameLen + 4);
}
// Forward current node
pclndCurrentNode = pclndNextNode;
}
}
}
////////////////////////////////////////////////////
// Connections.h
#ifndef CONNECTIONS_H
#define CONNECTIONS_H
#include "ClientNode.h"
class Connections
{
private:
// Data members
static Connections* _Instance;
int nNumOfConnections;
ClientNode* pclndFirst;
// Ctor
Connections();
public:
// Methods
void Add(Client clntNewClient);
void Remove(ClientNode* pclndClientToRemove);
int getNumOfConnections();
ClientNode* getFirst();
// Static methods
static Connections* getInstance();
};
#endif
////////////////////////////////////////////////////
// Connections.cpp
#include "Connections.h"
// Set instance to null
Connections* Connections::_Instance = NULL;
/* ------- PRIVATE CTOR -------
* [Description]: This method is the constructor of the Connections
* [Paramaters]: none
* [Return Value]: none
*/
Connections::Connections()
{
this->nNumOfConnections = 0;
this->pclndFirst = NULL;
}
/*
* [Description]: This method returns the amount of connections currently within our linked list
* [Paramaters]: none
* [Return Value]: The amount of connections
*/
int Connections::getNumOfConnections(){
return (this->nNumOfConnections);
}
/*
* [Description]: This method returns a pointer to the first client node within our connection list
* [Paramaters]: none
* [Return Value]: A pointer to the first client node
*/
ClientNode* Connections::getFirst()
{
return (this->pclndFirst);
}
/*
* [Description]: This method adds a new client to the linkedlist of clients
* [Paramaters]:
* clntNewClient - The new client struct
* [Return Value]: none
*/
void Connections::Add(Client clntNewClient)
{
// Create a new client node
ClientNode* pclndNewClientNode = new ClientNode;
// Set the client node's client
pclndNewClientNode->setClient(clntNewClient);
// Set the client node's next client pointer to point at the currenly first address
pclndNewClientNode->setNext(this->getFirst());
// Set the first client pointer to point at the new client node's address ( Push it within the linked list )
this->pclndFirst = pclndNewClientNode;
// Increase the number of connection
++(this->nNumOfConnections);
}
/*
* [Description]: This method removes a client from our linked list of connections
* [Paramaters]:
* pclndClientToRemove - The address of the client node we wish to remove
* [Return Value]: none
*/
void Connections::Remove(ClientNode* pclndClientToRemove){
// Variable definition
int nIndex;
ClientNode* pclndCurrentNode;
// Code section
pclndCurrentNode = this->getFirst();
// Checking if we need to remove the first node
if (pclndCurrentNode == pclndClientToRemove)
{
// Jump over deleted node
this->pclndFirst = pclndClientToRemove->getNext();
// Free memory
delete pclndClientToRemove;
// Decrease amount of connections
--(this->nNumOfConnections);
}
// We do not need to remove the first one
else
{
// Go through all ClientNodes addresses
for (nIndex = 0; nIndex < (this->nNumOfConnections - 1); ++nIndex)
{
// If the next node is the node we wish to delete
if (pclndCurrentNode->getNext() == pclndClientToRemove)
{
// Set the current node next node to be the next node of the node we wish to delete
pclndCurrentNode->setNext(pclndClientToRemove->getNext());
// free dynamically allocated memory
delete pclndClientToRemove;
// break outside the loop
break;
// Decrease amount of connections
--(this->nNumOfConnections);
}
// Next node is not the node we whish to delete
else
{
// Move to the next node
pclndCurrentNode = pclndCurrentNode->getNext();
}
}
}
}
/*
* [Description]: This method returns the only instance of Connections (SINGLETON PATTERN)
* [Paramaters]: none
* [Return Value]: A pointer to the single instance of connection
*/
Connections* Connections::getInstance(){
// If instance was not instantiated yet
if (_Instance == NULL)
{
// Call CTOR
_Instance = new Connections();
}
return (_Instance);
}
////////////////////////////////////////////////////
// ClientNode.h
#ifndef CLIENTNODE_H
#define CLIENTNODE_H
#include "Client.h"
class ClientNode
{
// Data members
Client clntClient;
ClientNode* pclntNextClient;
public:
// Access methods
void setNext(ClientNode* pclndNextNode);
void setClient(Client clntNewClient);
Client getClient();
ClientNode* getNext();
};
#endif
////////////////////////////////////////////////////
// ClientNode.cpp
#include "ClientNode.h"
/*
* [Description]: This method sets the next node our node would be pointing add
* [Paramaters]:
* pclndNextNode - The address of the next node we want this node to point at
* [Return Value]: none
*/
void ClientNode::setNext(ClientNode* pclndNextNode)
{
this->pclntNextClient = pclndNextNode;
}
/*
* [Description]: This method sets the client struct we want our current node to contain
* [Paramaters]:
* clntNewClient - New client
* [Return Value]: none
*/
void ClientNode::setClient(Client clntNewClient)
{
this->clntClient = clntNewClient;
}
/*
* [Description]: This method returns the client instance our node contains
* [Paramaters]: none
* [Return Value]: Our client
*/
Client ClientNode::getClient()
{
return (this->clntClient);
}
/*
* [Description]: This method returns the next node our node points at
* [Paramaters]: none
* [Return Value]: The address of the next node this node is pointing at
*/
ClientNode* ClientNode::getNext()
{
return (this->pclntNextClient);
}
////////////////////////////////////////////////////
// Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <WinSock2.h>
#define MAX_CLIENT_NAME_LEN = 40
class Client
{
// Data members
SOCKET scktClientSock;
char* szClientName;
bool bIsAdmin;
int nNameLength;
public:
// Access methods
void setClientSocket(SOCKET scktClientSock);
SOCKET getClientSocket();
void setClientName(char* szClientName);
char* getClientName();
void setIsAdmin(bool bIsAdmin);
bool getIsAdmin();
int getNameLength();
// Other methods
};
#endif
////////////////////////////////////////////////////
// Client.h
#include "Client.h"
/*
* [Description]: This method changes the SOCKET data member of the Client class
* [Paramaters]:
* _scktClientSock - the new socket client that is being set
* [Return Value]: none
*/
void Client::setClientSocket(SOCKET _scktClientSock)
{
this->scktClientSock = _scktClientSock;
}
/*
* [Description]: This method retrieves the client's socket
* [Paramaters]: none
* [Return Value]: The socket client
*/
SOCKET Client::getClientSocket()
{
return (this->scktClientSock);
}
/*
* [Description]: This method changes the client's name
* [Paramaters]:
* _szClientName - a zero terminated string that describes the new client's name
* [Return Value]: none
*/
void Client::setClientName(char* _szClientName)
{
// Variable definition
int nIndex = -1;
// Code section
this->szClientName = new char[41];
// Copy string char per char
do
{
++nIndex;
this->szClientName[nIndex] = _szClientName[nIndex];
} while (_szClientName[nIndex] != '\0');
// Name length is equal to index
this->nNameLength = nIndex;
}
/*
* [Description]: This method returns a pointer to the first char of the zero terminated client string
* [Paramaters]: none
* [Return Value]: a pointer to the string
*/
char* Client::getClientName()
{
return (this->szClientName);
}
/*
* [Description]: This method is used to set whether the client is an admin or not
* [Paramaters]:
* _bIsAdmin - a boolean indication of whether the user is an admin or not
* [Return Value]: none
*/
void Client::setIsAdmin(bool _bIsAdmin)
{
this->bIsAdmin = _bIsAdmin;
}
/*
* [Description]: This method determines whether the user is an admin or not
* [Paramaters]: none
* [Return Value]: A boolean indication of whether the user is an admin or not
*/
bool Client::getIsAdmin()
{
return (this->bIsAdmin);
}
/*
* [Description]: This method retrieves the client's name length
* [Paramaters]: none
* [Return Value]: the name length
*/
int Client::getNameLength()
{
return (this->nNameLength);
}
Again this is some really old code I've wrote I apologize if it's not so good but It definitely works... also notice that I've contains many different models within the server code, each are seperated by the following
////////////////////////////////////////////////////