habudu
habudu

Reputation: 303

non-terminating while loop, while using recv

I'm trying to receive a big message from a client program, both using SOCK_STREAM sockets, and as a result, I have to use a while loop on my recv call. However, it freezes, and doesn't seem to be printing anything inside the loop.

Here's the code for the server

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h> 
#include <stdlib.h>
#include <arpa/inet.h>

#define BUFSIZE 1024
extern int errno;
void error(char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
     int sock, newsock, len, fromlen, n;
     unsigned short port;
     struct sockaddr_in server, from;
     char buffer[BUFSIZE];
     char *msg = "I Got your message";

     if (argc < 2) {
         fprintf(stderr,"usage %s portnumber\n",argv[0]);
         exit(0);
     }
     port = (unsigned short) atoi(argv[1]);
     sock=socket(AF_INET, SOCK_STREAM, 0);
     if (sock < 0) error("Opening socket");
     server.sin_family=AF_INET;
     server.sin_addr.s_addr=INADDR_ANY;
     server.sin_port=htons(port);  
     len=sizeof(server);
     if (bind(sock, (struct sockaddr *)&server, len) < 0) 
          error("binding socket");
     fromlen=sizeof(from);
     if (listen(sock,5) < 0) 
          error("listening");

     // accept connections loop
     while (1) {
         newsock=accept(sock, (struct sockaddr *)&from, &fromlen);
         if (newsock < 0) error("Accepting");
         printf("A connection has been accepted from %s\n",
                 inet_ntoa((struct in_addr)from.sin_addr));

         // receive long messages loop
         while ((n = recv(newsock,buffer,BUFSIZE-1,0)) > 0){
            buffer[n] = '\0';
            printf("\n message: \n%s\n", buffer);
         }

        len = strlen(msg);
        n = send(newsock,msg,len,0);
        if (n < len) error("Error writing");

       if (close(newsock) < 0) error("closing");           
     }
     return 0; // we never get here 
}

And here's the code for the client

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> /* for atoi */

char *msg = "Very long message";
void error(char *msg)
{
perror(msg);
exit(0);
}

int main(int argc, char *argv[])
{
int sock, n;
unsigned short port;
struct sockaddr_in server;
struct hostent *hp;
char buffer[1024];

if (argc != 3) { 
     printf("Usage: %s server port\n", argv[0]);
     exit(1);
}
sock= socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) error("Opening socket");

server.sin_family = AF_INET;
hp = gethostbyname(argv[1]);
if (hp==NULL) error("Unknown host");
memcpy((char *)&server.sin_addr,(char *)hp->h_addr,         
      hp->h_length);
port = (unsigned short)atoi(argv[2]);
server.sin_port = htons(port);
if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
         error("Connecting");
n = send(sock, msg, strlen(msg),0);
printf("sent %d bytes\n", n);
if (n < strlen(msg))
         error("Writing to socket");

n = recv(sock, buffer, 1023,0);
if (n < 1) error("reading from socket");
buffer[n]='\0';
printf("The message from the server is %s\n",buffer);
if (close(sock) < 0) error("closing");
printf("Client terminating\n");

return 0;
}

The ultimate goal is to receive the full message on the server end, and then send a confirmation back to the client that the message was received.

Upvotes: 2

Views: 5138

Answers (1)

Ctx
Ctx

Reputation: 18420

This condition:

 (n = recv(newsock,buffer,BUFSIZE-1,0)) > 0

will always be true unless the remote closes the socket or the syscall returns with an error. Otherwise it will block, if no data is sent from the client.

On a STREAM socket you have to expect a byte stream without making assumptions, how it will be chunked across several calls to recv(). One common solution for that is to transmit (and receive) the length information first, and subsequently call recv until exactly length bytes are received in total.

Another option ("quick fix") for your special case could be to shutdown the sending direction of the socket in the client after sending the big message:

shutdown(fd, SHUT_WR);

After that, the recv syscall will indeed return with 0 and the server can send its confirmation. However, the client will not be able to send data after the call to shutdown().

Upvotes: 3

Related Questions