TecGuy94
TecGuy94

Reputation: 59

Multiple client TCP/IP server - server shows that clients disconnect with the same port number

Trying my hand at socket programming in C. When I connect multiple clients to the server and then disconnect them, the server shows that all clients are disconnecting on the same port.

Example: Connecting two clients and then disconnecting them.

./server 59001
Waiting for new connections....
Accepted new connection on 127.0.0.1:37098
Accepted new connection on 127.0.0.1:37100
Host disconnected: 127.0.0.1:37100
Host disconnected: 127.0.0.1:37100
Accepted new connection on 127.0.0.1:37102
Accepted new connection on 127.0.0.1:37106
Host disconnected: 127.0.0.1:37106
Host disconnected: 127.0.0.1:37106

You can see the server shows the two clients disconnecting on the same port. I'm using select for handling multiple clients, here is my server.c file:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>       /* memset */
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>    /* inet_addr */
#include <netinet/in.h>
#include <unistd.h>       /* close */
#include <sys/select.h>   /* select */

#define SIZE 1024

int socket_description(int port, struct sockaddr_in server_addr);

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: ./server <port>\n\n");
        exit(EXIT_FAILURE);
    }
    
    int port = atoi(argv[1]);
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;

    //create TCP socket
    int sock_fd = socket_description(port, server_addr);

    //set up synchronous i/o for handling multiple clients
    fd_set current_sockets, ready_sockets;     //create two sets of file descriptors to store, one to track our active connection (current_sock) and the other to hold temporary (ready_sock)
    FD_ZERO(&current_sockets);                 //initialise current sockets to zero
    FD_SET(sock_fd, &current_sockets);         //adds a file descriptor to the current socket set

    //handle client data
    char buffer[SIZE];
    char *p_buffer = buffer;
    int bytes_received;

    //accept incoming connections
    fprintf(stdout, "Waiting for new connections....\n");
    socklen_t sock_len = sizeof(server_addr);


    while (1)
    {

        //copy current set of fds
        ready_sockets = current_sockets;

        int ready = select(FD_SETSIZE, &ready_sockets, NULL, NULL, NULL);
        if (ready < 0)
        {
            perror("Could not read in ready_socket file descriptors (select error)");
            exit(EXIT_FAILURE);
        }
        

        //loop over the file descriptors that are ready to be read in
        for (int i = 0; i < FD_SETSIZE; i++)
        {
            if (FD_ISSET(i, &ready_sockets))
            {
                if (i == sock_fd)
                {
                    // this is a new connection to accept, set the new connection to the client structure
                    int client = accept(sock_fd,(struct sockaddr *)&client_addr, &sock_len);
                    if (client < 0)
                    {
                        perror("Error accepting incoming connection");
                        exit(EXIT_FAILURE);
                    } 
                    char const *client_ip = inet_ntoa(client_addr.sin_addr);
                    int client_port = ntohs(client_addr.sin_port);
                    fprintf(stdout, "Accepted new connection on %s:%d\n", client_ip, client_port);

                    // add the new client to the set
                    FD_SET(client, &current_sockets);
                } 
                else 
                {
                    // handle existing connection
                    if((bytes_received = recv(i,p_buffer,SIZE,0) > 0))
                    {
                        //check if the received message ends in a newline character, replace with null byte
                        if (*(p_buffer + bytes_received) == '\n')
                        {
                            *(p_buffer + bytes_received) = '\0';
                        }
                        char const *client_ip = inet_ntoa(client_addr.sin_addr);
                        fprintf(stdout, "Received message from %s: %s", client_ip, p_buffer);
                        int bytes_sent = send(i, p_buffer, bytes_received, 0);
                        if (bytes_sent < 0)
                        {
                            perror("Error receiving message");
                            exit(EXIT_FAILURE);
                        }  
                    }
                    // host disconnected
                    else
                    {
                        char const *client_ip = inet_ntoa(client_addr.sin_addr);
                        uint16_t client_port = ntohs(client_addr.sin_port);
                        fprintf(stdout, "Host disconnected: %s:%d\n", client_ip, client_port);
                        close(i);
                        FD_CLR(i, &current_sockets);
                    }
                }    
            }   
        }
    }

    return 0;

}

Upvotes: 0

Views: 173

Answers (1)

TecGuy94
TecGuy94

Reputation: 59

Fixed this by updating:

// host disconnected
else
{
   char const *client_ip = inet_ntoa(client_addr.sin_addr);
   uint16_t client_port = ntohs(client_addr.sin_port);
   fprintf(stdout, "Host disconnected: %s:%d\n", client_ip, client_port);
   close(i);
   FD_CLR(i, &current_sockets);
}

to the new version:

// host disconnected
else 
{
   getpeername(i , (struct sockaddr*)&client_addr, &client_len);          
   char const *client_ip = inet_ntoa(client_addr.sin_addr);
   uint16_t client_port = ntohs(client_addr.sin_port);
   fprintf(stdout, "Host disconnected: %s:%d\n", client_ip, client_port);
                        
   //close socket and remove client from fd_set
   close(i);
   FD_CLR(i, &current_sockets);
}

Upvotes: 0

Related Questions