Question

I'm trying to write a server in C using sockets which will be receiving commands from multiple clients. What I'm trying to understand is: if the client sends command which consists of, say, 4 characters, while recv() function is told to receive 5 bytes of data, what will happen?

Here's my code:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <dirent.h>

void *connection_handler(void *);
int sendall(int s, char *buf, int len, int flags);
int recvall(int s, char *buf, int len, int flags);

int main (int argc , char *argv[])
{
char check = 'n', *message;
int socket_d, port, socket_n, *sock_n, c;
struct sockaddr_in server_addr, client_addr;

socket_d = socket(AF_INET , SOCK_STREAM , 0);
if (socket_d == -1)
{
    printf("Could not create socket. Exiting...\n");
    return 1;
}

printf("Welcome to Random server v0.0.1.\n");

printf("Please input port (default - 3425): ");
scanf("%i", &port);
if (port >= 65536 || port < 0) port = 3425;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(port);

if( bind(socket_d,(struct sockaddr *)&server_addr , sizeof(server_addr)) < 0)
{
    printf("Binding socket failed.\n");
    return 1;
}
printf("Binding socket done.\n");

listen(socket_d, 3);

printf("Waiting for connections...\n");
c = sizeof(struct sockaddr_in);

while( (socket_n = accept(socket_d, (struct sockaddr *)&client_addr, (socklen_t*)&c)) )
    {
        puts("Connection accepted");
        message = "Hello Client, I have received your connection. And now I will assign a handler for you\n";
        send(socket_n , message , strlen(message), 0);

        pthread_t sniffer_thread;
        sock_n = malloc(1);
        *sock_n = socket_n;

        if( pthread_create( &sniffer_thread , NULL ,  connection_handler , (void*) sock_n) < 0)
        {
            perror("could not create thread");
            return 1;
        }

        //Now join the thread , so that we dont terminate before the thread
        //pthread_join( sniffer_thread , NULL);
        puts("Handler assigned");
    }

if (socket_n<0)
    {
        printf("Accept failed.\n");
        return 1;
    }
return 0;
}
void *connection_handler(void *socket_d)
{
printf("Thread created\n");
int sock = *(int*)socket_d;
int read_size;
char *message , client_message[200];

while (1)
{
    message = malloc(5);
    read_size = recv(sock , message , 5 , 0);
    strcpy(client_message, message);
    //send(sock , message , strlen(message), 0);
    free(message);

    if (strcmp(client_message, "list") == 0)
    {
        DIR * dir;
        struct dirent * de;
        strcpy(client_message, "");
        if ((dir = opendir(".")) == NULL ) strcpy(client_message, "Unable to open the directory.\n");
        else
        {
            while (de = readdir(dir)) {strcat(client_message, de->d_name); strcat(client_message, "\n"); }
        }
        closedir(dir);
        send(sock, client_message, strlen(client_message), 0);
        continue;
    }
    if (strcmp(client_message, "date") == 0)
    {
        time_t rtime;
        rtime = time (NULL);
        strcpy(client_message, ctime(&rtime));
        send(sock, client_message, strlen(client_message), 0);
        continue;
    }
    if (strcmp(client_message, "mkdir") == 0)
    {
        int result_code = mkdir("./new dir");
        if (result_code == 0) strcpy(client_message, "OK");
        else strcpy(client_message, "ERROR");
        send(sock, client_message, strlen(client_message), 0);
        continue;
    }
    if (strcmp(client_message, "exit") == 0)
    {
        strcpy(client_message, "Bye");
        send(sock, client_message, strlen(client_message), 0);
        break;
    }
    send(sock, client_message, strlen(client_message), 0);


    if(read_size == 0)
    {
        printf("Client disconnected\n");
        fflush(stdout);
        break;
    }
    else if(read_size == -1)
    {
        printf("Recv failed\n");
    }
}

printf("Thread will now be deleted.\n");
free(socket_d);

return 0;
}

As of now, it seems that it will write those 4 characters into buffer AND something else, to make it five bytes long. Am I correct? What exactly is happening? How could I make it so that server could process commands of any length?

Was it helpful?

Solution

As of now, it seems that it will write those 4 characters into buffer AND something else, to make it five bytes long. Am I correct?

No. recv() will read up to a maximum of the number of bytes requested, but may read less. From linked reference page:

...The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

The something else being witnessed is because the buffer recv() is populating is unitialized:

message = malloc(5); /* Uninitialized. */
read_size = recv(sock , message , 5 , 0);

Meaing if only 4 bytes are read the fifth byte in the buffer will contain a random value.

The posted code incorrectly uses strcpy(), which is dependent upon the presence of a null terminating character: use the return value of recv() to know exactly how many bytes were read and also if it fails completely (in which the case the return value is -1). For example:

read_size = recv(sock , message , 5 , 0);
if (read_size != -1)
{
    if (read_size == 4 && memcmp(message, "list", 4) == 0)
    {
    }
}
else
{
    /* Report failure. */
}

How could I make it so that server could process commands of any length?

To do this a mechanism is required to indicate the end of message. This could be a designated character that cannot appear inside a message or by prefixing the message with the length of the following message.

OTHER TIPS

If the client sends command which consists of, say, 4 characters, while recv() function is told to receive 5 bytes of data, what will happen?

The buffer will contain 4 bytes of data and the length information returned by recv() will tell you that there are 4 bytes of valid data and therefore there is nothing reliable in the fifth byte (but it is most probably what was there before the recv() call was made). Since your code does not send the null at the end of the null-terminated string, the receiving code does not get a null, so you cannot use strcpy() reliably on the received data. Use memmove() or memcpy() or perhaps strncpy() and then null terminate.

If you want to send arbitrarily long messages, you have two main options. One is to specify an upper bound on the length of a single message, allocate enough space to receive that, and carefully note how much was received. The other is to use a variant on TLV (type, length, value) format so the sender indicates the type and length of the data plus the actual data, and the receiver can read the type and length, allocate enough space, and then read the value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top