Question

I am trying to create a little Fileserver with the files only in the shared memory. The client is supposed to send commands to the server like CREATE, DELETE, etc. However, I am not at that stage yet.

I have prepared a server and a client. The server accepts a socket and created a new thread for each client connecting (requirement).

When I start a client, I can successfully connect to the server and send a message which will be received. However, this works only once. After my command has been sent the server will not receive any other commands.

I tried to catch the messages using a line breaker but this doesn't seem to work, so any help would be appreciated.

Thanks in advance.

Server Source:

/*
 * main.c - the server file. 
 *
 *  Created on: Apr 26, 2014
 *      Author: fish-guts
 */

#include "main.h"

/* our main server buffer */
char serverbuf[4096];
static int client_sock;

void launch_app(char *argv[]) {
    if ((strcmp(argv[1], "start")) == 0)
        startup();
    else if ((strcmp(argv[1], "stop")) == 0)
        stop_server();
    else {
        fprintf(stderr,
                "Invalid Command: %s. Valid Commands are     ./fileserver         [start|stop]\n",
                argv[1]);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char* argv[]) {
if (argc > 1)
    launch_app(argv);
    else {
        fprintf(stderr,
                "No argument supplied. Valid Argument are [start|stop]\n");
        exit(EXIT_SUCCESS);
    }
    return 0;
}

void print_start_msg(void) {
    fprintf(stderr, "###############################\n");
    fprintf(stderr, "Welcome to Severin'ŝ FileServer\n");
    fprintf(stderr, "###############################\n");
}

void stop_server(void) {
    exit(EXIT_SUCCESS);
}

void startup(void) {
    print_start_msg();
    start_server();
}

void start_server(void) {
    int s, len, rc;
    int tid;
    long t;
    char buf[100000];

    struct sockaddr_in addr;
    struct sockaddr_in client;
    pthread_t client_thread;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    unsigned short port = PORT;

    // clear the struct
    memset((char*) &addr, 0, sizeof(addr));

    fprintf(stderr, "\n\nStarting server...");
    // let's set some values

    /* type of socket created in socket() */
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons((unsigned short) PORT);

    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    /* bind the socket to the port specified above */

    if ((rc = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
    fprintf(stderr, "Error binding address: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Bind Successful\n");
}

if ((listen(sock, serverbuf) < 0)) {
    fprintf(stderr, "Listen Failed\n");
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Server started successfully, listening on port %d\n",
            PORT);
}
// the  main server loop
while (!quitting) {
    len = sizeof(struct sockaddr_in);

    client_sock = accept(sock, (struct sockaddr *) &client, &len);
    if (client_sock < 0) {
        fprintf(stderr, "Accept failed\n");
    } else {
        /* This is the client process */
        tid = pthread_create(&client_thread, NULL, doprocessing, (void*) t);
        if (tid) {
            fprintf(stderr, "Error creating thread: %d\n", tid);
        }
    }
}
}

void *doprocessing(void) {
    int n;

    int s, len, rc;
    char buf[100000];
    char *str = "Welcome\n";

    bzero(buf, 100000);
    n = write(client_sock, str, sizeof(str));

    if (n < 0) {
        fprintf(stderr, "ERROR writing to socket");
        exit(1);
    }
    s = recv(client_sock, buf, sizeof(serverbuf),0);
    if (s) {
        fprintf(stderr,"Bytes received: %i\n",s);
        buf[s] = 0;
        // we use CRLF as a line breaker, its easier to parse the commands
        char *pch = strtok(buf, "\n");
        while (pch != NULL) {
            strcpy(serverbuf, pch);
            fprintf(stderr, "Command: %s\n", buf);
            //TODO: add command handler
            //parse();
            serverbuf[s] = 0;
            pch = strtok(NULL, "\r\n");
            //addlog(1, serverbuf);
        }
    } else {
        fprintf(stderr,"No data received\n");
    }
}

Client Source:

#include "main.h"

#define PORT 8083
#define BUF_SIZE 1024

int quitting;

void start_client(const char *address);

int main(int argc, char* argv[]) {
    start_client("localhost");
}

void start_client(const char *address) {
    struct hostent *he;
    struct sockaddr_in server;
    int s;
    char buf[BUF_SIZE];
    char input[BUF_SIZE];
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if ((he = gethostbyname(address)) == NULL) {
        fprintf(stderr,"error resolving hostname..");
        exit(1);
    }

    /*
     * copy the network address part of the structure to the
     * sockaddr_in structure which is passed to connect()
     */
    memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
    server.sin_family = AF_INET;
    server.sin_port = htons((unsigned short)PORT);

    /* connect */
    if (connect(sock, (struct sockaddr *) &server, sizeof(server))) {
        puts("Error connecting...\n");
        exit(1);
    } else {
        fprintf(stderr,"Connected to server\n");
    }
    send(sock,"Hallo Server\n",BUF_SIZE,0);
    int i = 0;
    while(!quitting) {

        if(i<100) {
            send(sock,"yallo",6,0);
        }
        fgets(input, sizeof(input), stdin);
        if(send(sock,input,strlen(input),0)<0) {
            fprintf(stderr,"Error sending data: %s\n",errno);
        } else {
            fprintf(stderr,"Hello: %s\n",buf);
        }
        s = recv(sock, buf, sizeof(buf),0);
        if(s) {
            buf[s] = 0;
            fprintf(stderr,"Message from Server: %s\n",buf);
        } else {
            fprintf(stderr,"Error in recv: %s\n",errno);
        }
    }
}
Was it helpful?

Solution

Few rules when dealing with sockets:

1. Low-Level Message Field Definition

Always remember that you are reading and writing to and from stream of bytes. Another rule you have to remember that TCP/IP does not guarantee that one write equals one read. But, what these rules mean to you?

The combination of the above rules means that you have to know exactly how many bytes you need to read before you read from the socket. So:

  1. (Case 1): if you are reading a fixed-length field, then read as many bytes from the stream as in that field.
  2. (Case 2): And in the case if you want to read a variable-length field, then pre-pend that field with its byte count before sending.

(Case 1): For example, if you are sending/receiving a four-byte integer, then he code should look like:

int myInt = 12345;
send(sockfd, &myInt, sizeof(myInt), my_flags);

and on the read side:

int myReceivedInt = 0;
int received_count = recv(client_sock, &myReceivedInt, sizeof(myReceivedInt), 0);

Note: Different platforms define different sizes for data types.

(Case 2):

And if you are sending a variable-length field, such as a string, the code should look like:

// get string length
int str_size = strlen(my_str); 
// send fixed-length data to pre-pend variable-length field with the latter's size
send(sockfd, &str_size, sizeof(str_size), my_flags);  
// send the variable-length field.
send(sockfd, my_str, str_size, my_flags); 

and on the receive side:

int string_size = 0;
char *received_string = NULL;
int received_count = recv(client_sock, &string_size, sizeof(string_size), 0);
/*
  now we know how big the string is, so we allocate 
  memory for it or use a previously allocated buffer. This is omitted...
*/
int received_count = recv(client_sock, &received_string, string_size, 0);
received_string[string_size] = '\0';

Another Consequence of the above two rules, is that you may need to read more than once from a socket to receive one full message. This is especially true for larger messages (e.g. file transfer). So, when receiving large messages stick the recv() in a loop and keep reading until you have read the whole message.

For example if you are reading a file:

int file_size = 0, read_so_far = 0, ret = 0;
recv(sockfd, &file_size , sizeof(file_size), 0); 
// now we know how big is the file...allocate buffer (file_content) and read file fully
while(read_so_far != file_size)
{
   ret = recv(sockfd, file_content + read_so_far, file_size - read_so_far, 0); 
   if(ret < 0)
   {
      // handle error case, socket reset maybe?
      perror("Something bad happened with the socket");
      break;
   }
   read_so_far += ret;   
}

2. Defining a Protocol

Now that you know the low-level stuff, you can define the protocol. The protocol is a description of the format of messages to be sent a received over a connection. And each message is composed of one ore more fields like the ones above.

3. Defining a Message

Bear in mind that each message should define a command and arguments for the command. For example, a message to a delete a file should include the command "delete" and "file name". So, a delete file message may be defined as:

  • 1 byte for command + 4 bytes for name_size + variable-length file_name.

Do the same for other messages and you will end up with the protocol. Code may look like:

char command = 0;
s = recv(client_sock, &command, 1,0); 

if(command == LIST_NODES_COMMAND)
{
 // read arguments for command and process it.
} else if(command == CREATE_FOLDER_COMMAND)
{
 // read arguments for command and process it.
}

4. Message Handling Loop

The final step in the server is stick the message handling in a loop:

while(1)
{
    char command = 0;
    s = recv(client_sock, &command, 1,0);
    if(command == LIST_NODES_COMMAND)
    {
       // handle list command here.
    } else if(command == CREATE_FOLDER_COMMAND)
    {
       // read params like above and handle command.

    } else if (command == COMMAND_QUIT)
    {
       // do the stuff necessary before the client disconnects.
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top