Implementation of a Multi-User Chat Server Using TCP Socket Programming


Implementation of a Multi-User Chat Server Using TCP Socket Programming


Aim

To implement a multi-user chat server using TCP socket programming in C, where multiple clients communicate simultaneously through a central server using the select() system call.


Objectives

After completing this experiment, students should be able to

  • Understand TCP client-server communication.
  • Understand multi-client socket programming.
  • Learn I/O multiplexing using select().
  • Develop a simple chat server.
  • Broadcast messages from one client to all connected clients.

Theory

Multi-user Chat Server

A multi-user chat server is a centralized application that allows several clients to exchange messages simultaneously.

Every client establishes a TCP connection with the server.

Whenever a client sends a message,

  1. the server receives it,
  2. the server forwards (broadcasts) it to every other connected client.

Unlike a normal TCP server that communicates with only one client, a multi-user chat server must monitor several sockets simultaneously.


Why use TCP?

TCP provides

  • Reliable communication
  • Error checking
  • Ordered packet delivery
  • Flow control
  • Congestion control

Since chat applications cannot afford message loss or disorder, TCP is preferred over UDP.


Using select()

Normally,

recv(client1)

blocks until client1 sends data.

Suppose client2 sends a message.

The server cannot receive it because it is waiting for client1.

The select() system call solves this problem.

It continuously monitors multiple sockets simultaneously.

Whenever any socket becomes ready,

select() returns immediately.

The server then services only those sockets that contain data.


Working Principle

                 +---------------------+
                 |      Server         |
                 |    select() Loop    |
                 +----------+----------+
                            |
     ----------------------------------------------
     |                  |                 |
  Client 1          Client 2         Client 3
     |                  |                 |
     --------Messages Broadcast------------

Functions Used

FunctionPurpose
socket()    Creates socket
bind()    Associates socket with IP and port
listen()    Waits for client connections
accept()    Accepts new client
select()    Monitors multiple sockets
FD_SET()    Adds socket to monitoring list
FD_CLR()    Removes socket
recv()    Receives data
send()    Sends data
close()    Closes socket

Algorithm

Server

  1. Create a TCP socket.
  2. Bind it to a port.
  3. Listen for incoming connections.
  4. Initialize the master file descriptor set.
  5. Add the server socket to the descriptor set.
  6. Continuously execute the following:
    • Copy the master set to a temporary set.
    • Call select().
    • If the server socket is ready:
      • Accept a new client.
      • Add the client socket to the descriptor set.
    • Otherwise:
      • Receive the client's message.
      • Broadcast the message to all other clients.
    • Remove disconnected clients.

Client

  1. Create TCP socket.
  2. Connect to server.
  3. Create two execution paths:
    • Sender
    • Receiver
  4. Continue until user terminates.

Program

Server Program

/* server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/select.h>

#define PORT 9000
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024
#define NAME_SIZE 30

typedef struct
{
    int socket;
    char name[NAME_SIZE];
}Client;

Client clients[MAX_CLIENTS];

int main()
{
    int server_fd,new_socket;
    struct sockaddr_in address;
    socklen_t addrlen=sizeof(address);

    fd_set readfds;

    char buffer[BUFFER_SIZE];

    // Initialize client list
    for(int i=0;i<MAX_CLIENTS;i++)
    {
        clients[i].socket=0;
        strcpy(clients[i].name,"");
    }

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

    address.sin_family=AF_INET;
    address.sin_addr.s_addr=INADDR_ANY;
    address.sin_port=htons(PORT);

    bind(server_fd,(struct sockaddr *)&address,sizeof(address));

    listen(server_fd,5);

    printf("=====================================\n");
    printf(" Multi User Chat Server Started\n");
    printf(" Port : %d\n",PORT);
    printf("=====================================\n");

    while(1)
    {
        FD_ZERO(&readfds);

        FD_SET(server_fd,&readfds);

        int max_sd=server_fd;

        for(int i=0;i<MAX_CLIENTS;i++)
        {
            if(clients[i].socket>0)
            {
                FD_SET(clients[i].socket,&readfds);

                if(clients[i].socket>max_sd)
                    max_sd=clients[i].socket;
            }
        }

        select(max_sd+1,&readfds,NULL,NULL,NULL);

        // New client connection
        if(FD_ISSET(server_fd,&readfds))
        {
            new_socket=accept(server_fd,
                              (struct sockaddr *)&address,
                              &addrlen);

            char name[NAME_SIZE];

            recv(new_socket,name,sizeof(name),0);

            for(int i=0;i<MAX_CLIENTS;i++)
            {
                if(clients[i].socket==0)
                {
                    clients[i].socket=new_socket;
                    strcpy(clients[i].name,name);

                    printf("%s Connected\n",name);

                    break;
                }
            }
        }

        // Existing clients
        for(int i=0;i<MAX_CLIENTS;i++)
        {
            int sd=clients[i].socket;

            if(sd>0 && FD_ISSET(sd,&readfds))
            {
                int valread=recv(sd,buffer,sizeof(buffer)-1,0);

                if(valread<=0)
                {
                    printf("%s Disconnected\n",clients[i].name);

                    close(sd);

                    clients[i].socket=0;

                    strcpy(clients[i].name,"");
                }
                else
                {
                    buffer[valread]='\0';

                    char message[1200];

                    sprintf(message,"%s : %s",
                            clients[i].name,
                            buffer);

                    printf("%s",message);

                    // Broadcast to all other clients
                    for(int j=0;j<MAX_CLIENTS;j++)
                    {
                        if(clients[j].socket!=0 &&
                           clients[j].socket!=sd)
                        {
                            send(clients[j].socket,
                                 message,
                                 strlen(message),
                                 0);
                        }
                    }
                }
            }
        }
    }

    close(server_fd);

    return 0;
}

Client Program

/* client.c */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>

#define PORT 9000
#define BUFFER_SIZE 1024
#define NAME_SIZE 30

int main()
{
    int sock;

    struct sockaddr_in server;

    sock=socket(AF_INET,SOCK_STREAM,0);

    server.sin_family=AF_INET;
    server.sin_port=htons(PORT);
    server.sin_addr.s_addr=inet_addr("127.0.0.1");

    if(connect(sock,(struct sockaddr *)&server,sizeof(server))<0)
    {
        printf("Connection Failed\n");
        return 0;
    }

    char name[NAME_SIZE];

    printf("Enter your name : ");
    fgets(name,sizeof(name),stdin);

    name[strcspn(name,"\n")]='\0';

    send(sock,name,sizeof(name),0);

    printf("\nConnected to Chat Server\n");
    printf("Type your messages below\n\n");

    if(fork()==0)
    {
        // Sender Process

        char message[BUFFER_SIZE];

        while(1)
        {
            fgets(message,sizeof(message),stdin);

            send(sock,message,strlen(message),0);
        }
    }
    else
    {
        // Receiver Process

        char buffer[BUFFER_SIZE];

        while(1)
        {
            int n=recv(sock,buffer,sizeof(buffer)-1,0);

            if(n<=0)
            {
                printf("\nDisconnected from Server\n");
                break;
            }

            buffer[n]='\0';

            printf("%s",buffer);
        }
    }

    close(sock);

    return 0;
}

Compilation

gcc server.c -o server
gcc client.c -o client


Sample Execution

Terminal 1

./server
====================================
 Multi User Chat Server Started
 Port : 9000
====================================

Alice Connected
Bob Connected
Charlie Connected

Terminal 2

./client
Enter your name : Alice

Connected to Chat Server

Start chatting...

Terminal 3

./client
Enter your name : Bob

Connected to Chat Server

Start chatting...

Terminal 4

./client
Enter your name : Charlie

Connected to Chat Server

Start chatting...

Sample Output

Alice's Terminal

Hello Everyone

Bob : Good Morning

Charlie : Welcome to Computer Networks Lab

Bob's Terminal

Alice : Hello Everyone

Good Morning

Charlie : Welcome to Computer Networks Lab

Charlie's Terminal

Alice : Hello Everyone

Bob : Good Morning

Welcome to Computer Networks Lab

Server Terminal

====================================
 Multi User Chat Server Started
 Port : 9000
====================================

Alice Connected
Bob Connected
Charlie Connected

Alice : Hello Everyone

Bob : Good Morning

Charlie : Welcome to Computer Networks Lab

Program Explanation

Server

  1. Creates a TCP socket.
  2. Binds the socket to port 9000.
  3. Waits for incoming client connections.
  4. Uses the select() system call to monitor the server socket and all connected client sockets simultaneously.
  5. When a new client connects, it first receives the client name and stores it along with the corresponding socket descriptor.
  6. Whenever a client sends a message, the server prefixes the message with the client's name (for example, Alice : Hello Everyone) and broadcasts it to all other connected clients.
  7. If a client disconnects, the server removes the corresponding socket and client name from its list.

Client

  1. Creates a TCP socket.
  2. Connects to the server.
  3. Prompts the user to enter a client name (for example, Alice, Bob, or Charlie).
  4. Sends the client name to the server immediately after establishing the connection.
  5. Uses the fork() system call to create two execution paths:
    • Child Process: Continuously reads messages from the keyboard and sends them to the server.
    • Parent Process: Continuously receives and displays messages broadcast by the server.
  6. This enables the client to send and receive messages simultaneously, providing a real-time chat experience.

Result

A multi-user chat application was successfully implemented using TCP socket programming and the select() system call. Multiple clients connected concurrently to the server, exchanged messages in real time, and each message was displayed along with the sender's name, thereby simulating a simple group chat application.

Comments

Popular posts from this blog

Networks Lab PCCSL507 Semester 5 KTU CS 2024 Scheme - Dr Binu V P

Study of whois Command

Study and Use of ifconfig Command