0
votes

I'm writing a chat room program that communicates over network using TCP. If user provide ip address as a command line argument, the program would attempt to connect to that address. If not, server will wait for others to connect. The server has no problem receiving whatever text message the client send. However, the client side only receives text messages from server only when it sends its own message. How do I fix that so that client side receives messages right away? This is my code

Server code:

#define MAX_CLIENTS 100

static unsigned int cli_count = 0;
static int uid = 10;

typedef struct {
    struct sockaddr_in addr;    
    int connfd;         
    int uid;            
    char name[32];          
} client_t;

client_t *clients[MAX_CLIENTS];

void queue_add(client_t *cl)
{
    int i;
    for(i=0;i<MAX_CLIENTS;i++)
    {
        if(!clients[i])
        {
            clients[i] = cl;
            return;
        }
    }
}

void queue_delete(int uid)
{
    int i;
    for(i=0;i<MAX_CLIENTS;i++)
    {
        if(clients[i])
        {
            if(clients[i]->uid == uid)
            {
                clients[i] = NULL;
                return;
            }
        }
    }
}
void send_message_all(char *s)
{

    int i;
    for(i=0;i<MAX_CLIENTS;i++)
    {
        if(clients[i])
        {
            write(clients[i]->connfd, s, strlen(s));
        }
    }
}
void *hanle_client(void *arg)
{
    char buff_in[256];
    char buff_out[256];
    int rlen;
    cli_count++;
    client_t *cli = (client_t *)arg;
    sprintf(buff_out, "<<JOIN, HELLO %s\r\n", cli->name);
    send_message_all(buff_out);

    bzero(buff_in,sizeof(buff_in));

    while((rlen = read( cli->connfd,buff_in,sizeof(buff_in)-1))>0)
    {
        sprintf(buff_out, "[%s] %s\r\n", cli->name, buff_in);
        send_message_all(buff_out);
    }


    close(cli->connfd);

    /* Delete client from queue and yeild thread */
    queue_delete(cli->uid);
    free(cli);
    cli_count--;
    pthread_detach(pthread_self());

    return NULL;
}

int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0, portno;
    struct sockaddr_in serv_addr;
    struct sockaddr_in cli_addr;
    pthread_t tid;
    if (argc < 2) {
         printf("ERROR, no port provided\n");
         exit(1);
     }


    //Create socket
    listenfd= socket(AF_INET , SOCK_STREAM , 0);
    if (listenfd == -1)
    {
        printf("Could not create socket");
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = atoi(argv[1]);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(portno);

    /* Bind */
    if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("Socket binding failed");
        return 1;
    }

    /* Listen */
    if(listen(listenfd, 10) < 0)
    {
        perror("Socket listening failed");
        return 1;
    }

    printf("<[SERVER STARTED]>\n");
    socklen_t clilen = sizeof(cli_addr);
    /* Accept clients */
    while( (connfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen)))
    {
        /* Client settings */
        client_t *cli = (client_t *)malloc(sizeof(client_t));
        cli->addr = cli_addr;
        cli->connfd = connfd;
        cli->uid = uid++;
        sprintf(cli->name, "%d", cli->uid);

        /* Add client to the queue and fork thread */
        queue_add(cli);
        pthread_create(&tid, NULL, &hanle_client, (void*)cli);
    }
}

Client code:

int main(int argc , char *argv[])
{
    int sockfd,  portno ;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    char message[2000],server_reply[2000];
    if (argc <3) 
    {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(1);
    }
    portno = atoi(argv[2]);

    //Create socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

   if (sockfd < 0)
   {
      perror("ERROR opening socket");
      exit(1);
   }
   server = gethostbyname(argv[1]);

   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(1);
   }

   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);

    //Connect to remote server
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
   {
      perror("ERROR connecting");
      exit(1);
   }

    puts("Connected\n");

    //keep communicating with server
    while(1)
    {
        //Receive a reply from the server
        bzero(server_reply,2000);
        if( recv(sockfd , server_reply , 2000,0) < 0)
        {
            puts("recv failed");
            break;
        }
        printf("%s", server_reply);
        server_reply[0]='\0';

        //Send Message to server
        printf("Enter Message:");
        bzero(message,2000);
        fgets(message, sizeof(message),stdin);

        if(send(sockfd , message , strlen(message),0) < 0)
        {
            puts("Send failed");
            return 0;
        }


    }

    close(sockfd);
    return 0;
}
1

1 Answers

0
votes

I am not sure if I understood your problem correctly. But at a high level, I noticed that your hanleClient method calls close(cli->connfd) on the clients socket after calling sendall. After calling close, you are deleting the client details from the queue. This way, the client being deleted will never receive any future messages. Are you sure this is what you want?

Try removing these lines and check if that is what you want -

close(cli->connfd);

/* Delete client from queue and yeild thread */
queue_delete(cli->uid);
free(cli);
cli_count--;

This way, whenever the server receives a message, it will try to send it to all clients that are connected to the server.

Note: Your code is not thread safe and will result in unexpected behaviour since you are accessing global data from within threads without using mutexes.