espo92
espo92

Reputation: 23

Basic Socket Programming in C

So I am having a weird problem that I have been facing in my program that is basically a client/server socket programming example in C. I have taken time away from the problem, but I cannot seem to locate what is causing the problem.

If needed, I can post more details, but following is what is not working.

       while(1)
{
char word[256];
gets(word);
printf("%s",word);

     if(strcmp(word, "TRANSLATE") == 0 || strcmp(word, "GET") == 0 ||strcmp(word, "STORE") == 0 || strcmp(word, "EXIT") == 0)
{
    if(strcmp(word, "TRANSLATE") == 0)
    {
        if(send(sock , word , strlen(word) , 0)<0)
        {
            printf("Send Error\n");
            return 1;
        }
        bzero(server_reply,2000);
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out 200ok message
        printf("%s\n", server_reply);
        //Get the first word
        gets(word2);
        while(strcmp(word2,"." )!= 0)
        {

            if(send(sock , word2 , strlen(word2) , 0)<0)
            {
                printf("Send Error\n");
                return 1;
            }
            gets(word2);    
        }
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
            printf("%s\n", server_reply);
        //STILL NEED TO WORK ON PARSING OUT SPACES BECAUSE RESULT IS IN A STRING, JUST WITH SPACES.
        //ALSO NEED TO DO SERVER SIDE CAPITALIZATION
    }
    if(strcmp(word, "GET") == 0)
    {

        printf("Getting");
    /*
        if(send(sock , word , strlen(word) , 0)<0)
        {
            printf("Send Error\n");
            return 1;
        }
        bzero(server_reply,2000);
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out 200ok message
        printf("%s\n", server_reply);
        /*
         if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out the KANSAS message
        printf("%s\n", server_reply);
        */
    }
}

The code breaks once I uncomment in the "GET" selection, and it doesn't even hit the printf() statement before the if statement.If I leave it out it hits all the proper statements perfectly. Am I missing something? Also, I am thinking it may be something with the server communication, so this is the server code.

  #include<stdio.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;
   struct sockaddr_in server , client;
   char client_message[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( 5149 );

   //Bind
   if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
   {
       //print the error message
       printf("bind failed. Error");
       return 1;
   }
   puts("bind done");

   //Listen
   listen(socket_desc , 3);

   //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)
   {
       printf("accept failed");
       return 1;
   }
   puts("Connection accepted");

   //Receive a message from client
   while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
   {
    if(strcmp(client_message,"TRANSLATE")==0)
    {
        *(client_message + read_size) = '\0';
        char word[256]="200 OK TRANSLATE";
        write(client_sock, word, strlen(word));
        /*
         while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
         {
            *(client_message + read_size) = '\0';
            puts(client_message);
            write(client_sock,client_message,strlen(client_message));
            if(strcmp(client_message,".")==0)
            {
                printf("breaking");
                break;
            }
            printf("IN while loop");
         }

        printf("Out of while loop");
        */
    }
    if(strcmp(client_message,"GET")==0)
    {
        *(client_message + read_size) = '\0';
        char word[256]="200 OK GET";
        write(client_sock, word , strlen(word));
        char word2[256]="I don't think we're in Kansas anymore.";
        write(client_sock, word2 , strlen(word2));
    }
    if(strcmp(client_message,"STORE")==0)
    {
        char word[256]="200 OK STORE";
        write(client_sock, word , strlen(word));
    }
   }

   if(read_size == 0)
   {
       puts("Client disconnected");
       fflush(stdout);
   }
   else if(read_size == -1)
   {
       printf("recv failed");
   }

   return 0;
}

Upvotes: 0

Views: 359

Answers (1)

You should add a \n at end of every printf format control string and/or call fflush(3)

The main issue in your program is the wrong belief that a given send (or write) to a socket(7) corresponds to a single recv (or read) on the other side.

A tcp(7) implementation is just a stream of bytes ; there is no implicit message boundaries. So your application should buffer and explicitly split that stream into meaningful messages.

It could happen, and it does happen, that one side is able to send or write 4000 bytes at once followed by another emission of 1000 bytes, but the other side would first recv (or read) e.g. 39 bytes, then 1 byte, then 1960 bytes then 3000 bytes. You then understand that "messages" are not intact, there is only a stream of bytes.

An additional difficulty is that such behavior is not reproducible. The next time you'll run your server and client programs they will behave differently!

In practice, it is helpful to define well where are the message boundaries. You could do like in HTTP, i.e. put some content length (in HTTP, the Content-Length: attribute in reply headers) in the beginning of each message. You could also decide that a message is a single long line with a single newline terminating it (and then, you could need a convention to escape internal newlines), look e.g. at JSON. And of course you should buffer on both sides.

You might want to test the readability or writability of a socket (or a pipe) with a multiplexing call like poll(2) (perhaps in your event loop)

I strongly suggest to read Advanced Linux Programming and some Linux Socket Tutorial.

BTW, always test your syscalls for failure. See intro(2) and use perror(3) or errno(3) (often with strerror(3)).

Of course compile with all warnings and debug info (gcc -Wall -g) and use the debugger (gdb) (and perhaps also strace(1)). You might want to debug with two gdb sessions (one for the server, one for the client) in different terminals.

BTW, don't use the obsolete deprecated bzero function, use the standard memset(3) instead.

Upvotes: 3

Related Questions