0

I am trying to implement a TCP server that can handle multiple connections at a time in C using pthreads.


Server code

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<pthread.h>
#include<readline/readline.h>
void* connection_handler(void *arg)
{
    int sock_number = *((int *)arg);
    printf("New connection with socket No. : %d\n",sock_number);
    char *message = "!!!!!!!!!!!!!!!!!! MESSAGE !!!!!!!!!!!!!!!!!!\n";
    while(1)
    {
        write(sock_number,message,strlen(message));
    }
}
int main()
{
    int sock_desc;
    sock_desc = socket(AF_INET,SOCK_STREAM,0);
    if(sock_desc<0)
    {
        perror("Socket error");
    }
    else
    {
        printf("Socket created!\n");
        struct sockaddr_in server,client;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(8080);
        if(bind(sock_desc,(struct sockaddr*)&server,sizeof(server))<0)
        {
            perror("Binding error");
        }
        else
        {
            printf("Socket bound to 8080 port!\n");
            printf("Listening started.........\nWaiting for connections from clients.........\n");
            listen(sock_desc,20);
            int sock_size = sizeof(struct sockaddr_in);
            int new_socket;
            pthread_t pid[50];
            int connection_count=0;
            while((new_socket = accept(sock_desc,(struct sockaddr*)&client,(socklen_t*)&sock_size)))
            {
                printf("New connection : %s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                if(pthread_create(&pid[connection_count++],NULL,connection_handler,&new_socket)!=0)
                {   
                    perror("Thread error");
                }
            }
            for(int i=0;i<connection_count;i++)
            {
                pthread_join(pid[i],NULL);
            }
        }
    }
}

The problem is that when one of the client is stopped , the whole server program will be terminated at the same time and also all other clients.But there is no problem with having multiple connections.Anybody please tell why the server is terminating and is there any way to keep the server running even if the client terminates?

JOSEPH BENOY
  • 149
  • 1
  • 6
  • 3
    Looks like `while((new_socket = accept(sock_desc,(struct sockaddr*)&client,(socklen_t*)&sock_size)))` will loop until `accept` returns zero. 0 may be an acceptable socket and accept returns a negative number on error. Duhr. No, 0 should be stdin. I don't think you'll ever see 0. – user4581301 Mar 31 '21 at 19:33
  • 5
    The thread continually writes to the socket. If the program writes to a socket that has been closed, you should get a SIGPIPE, and if you don't handle it correctly, the program will crash. – user4581301 Mar 31 '21 at 19:38
  • 1
    Does [How to prevent SIGPIPEs (or handle them properly)](https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly) help at all? – user4581301 Mar 31 '21 at 19:42
  • 1
    Sorry I was focusing on the wrong part of the code. I should have caught that a lot earlier. – user4581301 Mar 31 '21 at 19:43
  • @user4581301 buddy thank you so much . I solved it by handling SIGPIPE . You saved my life<3 – JOSEPH BENOY Mar 31 '21 at 19:46
  • 4
    Don't forget to check the return code from `write` so you can handle the errors it spits out. – user4581301 Mar 31 '21 at 19:48
  • 4
    ',&new_socket' no, not safe. A second connetion may mutate new_socket before the handler thread can dereference the pointer to it:( – Martin James Mar 31 '21 at 19:50
  • @user4581301 Ok buddy , I will update it – JOSEPH BENOY Mar 31 '21 at 19:50
  • 3
    The only other thing that stands out is that you pass `new_socket` by reference to the thread routine. Hence, if multiple clients connect in rapid succession there's a chance they will all make use of the same socket descriptor. – G.M. Mar 31 '21 at 19:50
  • @G.M. buddy I solved the issue by handling SIGPIPE signal. The code works fine . Thanks a lot<3 – JOSEPH BENOY Mar 31 '21 at 19:54
  • @martin-james Ok bro let me try using thread locks.Thank you<3 – JOSEPH BENOY Mar 31 '21 at 19:56

1 Answers1

2

Let's take a look at this code snippet:

 while((new_socket = accept(sock_desc,(struct sockaddr*)&client,(socklen_t*)&sock_size)))
        {
            printf("New connection : %s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
            if(pthread_create(&pid[connection_count++],NULL,connection_handler,&new_socket)!=0)
            {   
                perror("Thread error");
            }
        }
        for(int i=0;i<connection_count;i++)
        {
            pthread_join(pid[i],NULL);
        }

You have this variable new_socket. You have it's pointer &new_socket. When Client 1 connects, you pass &new_socket to the pthread, Thread 1.

You then get Client 2 connecting. Your variable new_socket, with the same address: &new_socket will then be passed to Thread 2. This effectively means when you accept a new connection, you are overwriting a variable used by your previous socket thread.

This means if a second client connects, before Thread 1 for Client 1 executes int sock_number = *((int *)arg); They will cause the execution thread to rewrite the value passed to Thread 1, and both clients will be referred to by the same socket

When one client closes the socket, they close the same socket used by every other client.

Your mistake arises from the idea that a new socket object is instantiated by accept, with a new address. This is not the case, as C has no objects. Really your only reference to this socket file descriptor is 'int new_socket` Which is a variable that keeps the same address throughout your while loop.

When you execute new_socket = accept(sock_desc,(struct sockaddr*)&client,(socklen_t*)&sock_size This simply copies the value of the file descriptor for the socket to new_socket. A simply way to solve this would be to pass the socket by value:

void* connection_handler(void *arg)
{ 
 int sock_number = ((int)arg)
    ...
}

...

if(pthread_create(&pid[connection_count++],NULL,connection_handler,(void *)new_socket)!=0)
Pixel
  • 31
  • 1
  • 5