2

I have a problem with a socket. I have a server and a client.

The purpose of the program:

Client/server connection (both send messages each others) Client sends a message; Server reads the message; Server sends back the message to the client.

But I have a deadlock because both client and server wait for receiving a message. The code:

Server.c

message = "Client connection handler ok\n";
write(sock , message , strlen(message));

message = "type something and i'll repeat what you type \n";
write(sock , message , strlen(message));

//Receive a message from client
while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
{
    puts(client_message);

    //Send the message back to client
    write(sock , client_message , strlen(client_message));

    bzero(client_message,2000);
}

Client.c

//Here I print the two messages You can see server side code:
//- Client connection handler ok
//- type something and i'll repeat what you type
while( (read_size = recv(sockfd , client_message , 2000 , 0)) > 0 )
{
     printf("%s\n",client_message);
     bzero(client_message,2000);
}

In the execution of the code I remain blocked in both client and server inside the while.

How can I solve this problem?

alk
  • 69,737
  • 10
  • 105
  • 255
Simone
  • 2,304
  • 6
  • 30
  • 79
  • 7
    The client loop is never writing to the server, so it seems obvious from your code that the server will sit there waiting. And then, if it waits for data, it will never write either, so the client will wait too. – SirDarius Aug 04 '14 at 13:29
  • Yes, but I dont know how can I solve it... If i insert an `fgets` inside the while i block the loop... and it's not good... How you can see Im sending two messages from server to client... If i put the `fgets` inside the while i'd receive just the first one... so i don't know how can i solve this situation... – Simone Aug 04 '14 at 13:45
  • 2
    You might like to read about `select()` and/or `poll()`. Also there are **no** messages on TCP level. TCP is **stream** oriented. To do messaging you need to define and implement a protocol on top of/using the bidirectional TCP stream. – alk Aug 04 '14 at 16:53
  • Security bug - Even though you zero our your buffer before each recv call, both your client and your server code *assumes* the received messages are null terminated (zero byte as last char). My malicious client (or server) could send a 2000 byte message to the other node without a null char at the end. Easy fix would be to simply pass 1999 as the length to recv() and then set `client_message[read_size] = '\0'` after checking that `read_size is >= 0` – selbie Aug 04 '14 at 17:19

2 Answers2

1

You're looking for the select function. You tell select to wait on both stdin and your socket.

Your client will look like this (untested code):

/* This is a bit mask where each bit corresponds to a file descriptor.
   It is an in/out parameter, so you set some bits to tell select what
   to wait on, and then select sets bits in it to tell you what fd has
   data waiting for you to read. */
fd_set read_fds;

while(1){
    int fd_max = STDIN_FILENO;

    /* Set the bits for the file descriptors you want to wait on. */
    FD_ZERO(&read_fds);
    FD_SET(STDIN_FILENO, &read_fds);
    FD_SET(sockfd, &read_fds);

    /* The select call needs to know the highest bit you set. */    
    if( sockfd > fd_max ) { fd_max = sockfd; }

    /* Wait for any of the file descriptors to have data. */
    if (select(fd_max + 1, &read_fds, NULL, NULL, NULL) == -1)
    {
      perror("select:");
      exit(1);
    }

    /* After select, if an fd's bit is set, then there is data to read. */      
    if( FD_ISSET(sockfd, &read_fds) )
    {
        /* There is data waiting on your socket.  Read it with recv(). */
    }

    if( FD_ISSET(STDIN_FILENO, &read_fds )
    {
        /* The user typed something.  Read it fgets or something.
           Then send the data to the server. */
    }
}

Don't forget after you read from stdin to send the data to your server. In the code you've posted, you haven't actually sent any data to your server, so the server is not going to wake up.

indiv
  • 17,306
  • 6
  • 61
  • 82
  • Almost done.... Everything look like to work... just a doubt on the `while(1)`... when i close connection from server, the loop continues, so i think i need something like `while(connection is open)`. Any idea? Ps.: Does not exists a reference like msdn to know which method are inside a library? – Simone Aug 05 '14 at 11:24
  • Yes, I would loop on a variable. You could do something like detect when the user types /quit for example and then change the variable to exit the loop. – indiv Aug 06 '14 at 04:03
0

A minimal protocol would be implemented by the following:

//Here I print the two messages You can see server side code:
//- Client connection handler ok
//- type something and i'll repeat what you type
while( (read_size = recv(sockfd , client_message , 2000 , 0)) > 0 )
{
  printf("%s\n",client_message);
  bzero(client_message,2000);

  if (0 == strcmp(client_message, "type something and i'll repeat what you type \n")
  {
    char buffer[256] = {0};
    fgets(buffer, sizeof(buffer), stdin);
    write(sockfd, buffer, strlen(buffer)+1);
  }
}

Any error checking in the example above had been omitted for the sake of readabilty.

If (luckly) the part of the byte stream requesting the client to type anything had been received (as one fragment), fgets() is called to read in a line from standard input which then is tried to be sent out via the socket.


Also please note that reading/writing from/to a socket not necessarily read/writes a much bytes as the call was told to read/write. So to make sure the requested amount of data had been read/written the code needs to loop around such calls inspecting their result until the requested amount of data finally had been transmitted. More on this including links to answers showing how to do this here: https://stackoverflow.com/a/24536689/694576

Community
  • 1
  • 1
alk
  • 69,737
  • 10
  • 105
  • 255