1

I am trying to write a server that can handle at most 5 concurrent clients.
Whenever a client gets successfully connected to the server & the number of clients is less than or equal to 5, the server sends a welcome message, generates a 5 digit unique random number for identifying that client, sends this number to the client and prints this number in the console.If the number of clients tends to be greater than 5, then for each new request, it just sends a message "Connection Limit Exceeded" to the client & closes the connection.
Client just prints the messages sent by the server.
The problem I'm facing is that, the random number is not being propagated properly to the client.Few times the client prints the same number as generated by the server but few times the client just prints 0(as the variable storing incoming value of that random number is initialized to 0).
What could be the reason behind this?

Here are the codes for client and server: server:

/* A simple server in the internet domain using TCP
   The port number is passed as an argument 
   This version runs forever, forking off a separate 
   process for each connection
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <stdlib.h>

void dostuff(int); /* function prototype */
void write_once (int sock);
void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno, pid, count = 0;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     while (1) {
         newsockfd = accept(sockfd, 
               (struct sockaddr *) &cli_addr, &clilen);
         if (newsockfd < 0) 
             error("ERROR on accept");
         pid = fork();
         count++;
         if (pid < 0)
             error("ERROR on fork");
         if (pid == 0 && count <=5 )  {
             close(sockfd);
             dostuff(newsockfd);
             exit(0);
         }
         if (pid == 0 && count >= 5 )  {
             close(sockfd);
             write_once(newsockfd);
             exit(0);
         }
         else close(newsockfd);
     } /* end of while */
     close(sockfd);
     return 0; /* we never get here */
}

/******** DOSTUFF() *********************
 There is a separate instance of this function 
 for each connection.  It handles all communication
 once a connnection has been established.
 *****************************************/
void dostuff (int sock)
{
   int n;
   char buffer[256];

   bzero(buffer,256);
   n = write(sock,"Welcome\n",8);
   if (n < 0) error("ERROR writing to socket");

   srand((unsigned int)time(NULL));
   int r = rand() % 90000 + 10000; 
   int converted_r = htonl(r);

   n = write(sock, &converted_r, sizeof(converted_r));
   if (n < 0) error("ERROR writing to socket");
   printf("%d\n", r);
}

void write_once (int sock)
{
   int n;
   char buffer[256];   
   bzero(buffer,256);
   n = write(sock,"Connection Limit Exceeded!!",28);
   if (n < 0) error("ERROR writing to socket");
}

client:

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

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    int received_int = 0;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");

    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);

    n = read(sockfd, &received_int, sizeof(received_int));
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%d\n", ntohl(received_int)); 
    close(sockfd);
    return 0;
}

Reference

Romy
  • 329
  • 1
  • 11
  • 3
    TCP functions read (or write) may return 0, which your code doesn't handle properly. And they may return less than the requested byte count, which is not an error. In this case call the function again until you received as many bytes as desired. See also http://stackoverflow.com/questions/666601/what-is-the-correct-way-of-reading-from-a-tcp-socket-in-c-c – sb9 Jan 24 '17 at 14:23

1 Answers1

2

The issue is that TCP is a stream oriented protocol, and not packet oriented. So it may happen that

  • The first read() of the client reads what the first write() of the server sent ("Welcome")
  • The second read() of the client reads what the second write() of the server sent (Your number)

This is what you expect and what sometimes happens.

However, it might also be that the client reads the data of both writes of the server at once! This usually happens when

  • either the server aggregated the two writes to a single tcp-packet
  • or the client reads the data after both tcp segments with data arrived

You cannot make sure what happens and cannot rely on any specific behaviour.

How to fix this depends solely on your protocol. If the first message is always "Welcome\n", then try to read only 8 bytes first. If you happen to read n < 8 bytes, you have to retry and read 8-n bytes to get the rest of the message. Subsequently read sizeof(received_int) bytes, also watching for the real number of bytes received.

If the message is of variable length you will have to use some kind of framing like a preceding length-byte or something like that.

Ctx
  • 18,090
  • 24
  • 36
  • 51