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,
- the server receives it,
- 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
| Function | Purpose |
|---|---|
| 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
- Create a TCP socket.
- Bind it to a port.
- Listen for incoming connections.
- Initialize the master file descriptor set.
- Add the server socket to the descriptor set.
-
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
- Create TCP socket.
- Connect to server.
-
Create two execution paths:
- Sender
- Receiver
- 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
- Creates a TCP socket.
- Binds the socket to port 9000.
- Waits for incoming client connections.
-
Uses the
select()system call to monitor the server socket and all connected client sockets simultaneously. - When a new client connects, it first receives the client name and stores it along with the corresponding socket descriptor.
- 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.
- If a client disconnects, the server removes the corresponding socket and client name from its list.
Client
- Creates a TCP socket.
- Connects to the server.
- Prompts the user to enter a client name (for example, Alice, Bob, or Charlie).
- Sends the client name to the server immediately after establishing the connection.
-
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.
- 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
Post a Comment