Creating Server Script
In this section, we will create the server script in C language. So follow the steps and break down the script to understand the implementation of the server.
Step 1: Create a server.c File
Firstly, create the server.c file in the nano editor by using the command in the terminal.
nano server.c
Step 2: Server Initialization and Socket Binding
C
int serverSocket; struct sockaddr_in serverAddr; char buffer[MAX_MESSAGE_SIZE]; socklen_t clientLen = sizeof ( struct sockaddr_in); ssize_t bytesRead; // Create socket if ((serverSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror ( "Socket creation failed" ); exit (EXIT_FAILURE); } // Configure server address memset (&serverAddr, 0, sizeof (serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(PORT); // Bind socket if (bind(serverSocket, ( struct sockaddr*)&serverAddr, sizeof (serverAddr)) == -1) { perror ( "Bind failed" ); exit (EXIT_FAILURE); } // Initialize mutex pthread_mutex_init(&mutex, NULL); printf ( "Server listening on port %d...\n" , PORT); |
- Socket Creation: The server uses the socket system call to create a UDP socket.
- Server Address Configuration: The server’s address structure (serverAddr) is configured with the server’s IP, port, and protocol details.
- Binding: The socket is bound to the server’s address using the bind system call.
- Mutex Initialization: A mutex (pthread_mutex_t mutex) is initialized for thread synchronization.
- Print Server Listening: A message is printed indicating that the server is listening on a specific port.
Step 3: Handling Clients – Main Loop
C
// Create a thread for each client while (1) { // Receive message from client bytesRead = recvfrom(serverSocket, buffer, sizeof (buffer), 0, ( struct sockaddr*)&clients[clientCount].address, &clientLen); if (bytesRead == -1) { perror ( "Receive failed" ); exit (EXIT_FAILURE); } buffer[bytesRead] = '\0' ; printf ( "Client %d connected: %s" , clientCount + 1, buffer); // Add client to the list clients[clientCount].socket = serverSocket; clients[clientCount].id = clientCount; pthread_create(&threads[clientCount], NULL, handleClient, ( void *)&clients[clientCount].id); clientCount++; // Send a welcome message to the client (for demonstration purposes) sprintf (buffer, "Server: Welcome, you are Client %d\n" , clientCount); sendto(clients[clientCount - 1].socket, buffer, strlen (buffer), 0, ( struct sockaddr*)&clients[clientCount - 1].address, sizeof (clients[clientCount - 1].address)); } |
- Client Connection Handling: Inside an infinite loop, the server continuously receives messages from clients using recvfrom.
- Client Information Printing: Information about the connected client is printed, and the client’s message is stored in the buffer.
- Client Registration: The client’s socket, id, and address are stored in the client’s array.
- Thread Creation: A new thread is created for each connected client using pthread_create, and the handleClient function is called.
- Welcome Message: A welcome message is sent to the client for demonstration purposes.
Step 4: Handling Individual Clients in Threads
C
void *handleClient( void *arg) { int id = *(( int *)arg); char buffer[MAX_MESSAGE_SIZE]; ssize_t bytesRead; while (1) { // Receive message from client bytesRead = recvfrom(clients[id].socket, buffer, sizeof (buffer), 0, NULL, NULL); if (bytesRead == -1) { perror ( "Receive failed" ); exit (EXIT_FAILURE); } buffer[bytesRead] = '\0' ; printf ( "Client %d: %s" , id + 1, buffer); // Broadcast the received message to all clients pthread_mutex_lock(&mutex); for ( int i = 0; i < clientCount; ++i) { if (i != id) { sendto(clients[i].socket, buffer, bytesRead, 0, ( struct sockaddr*)&clients[i].address, sizeof (clients[i].address)); } } pthread_mutex_unlock(&mutex); // Send a message to the client (for demonstration purposes) sprintf (buffer, "Server: Message from server to Client %d\n" , id + 1); sendto(clients[id].socket, buffer, strlen (buffer), 0, ( struct sockaddr*)&clients[id].address, sizeof (clients[id].address)); } } |
- Thread Function Definition: The handleClient function is defined to handle communication for each connected client in a separate thread.
- Message Reception: The thread continuously receives messages from its corresponding client using recvfrom.
- Message Broadcast: The received message is broadcasted to all clients except the sender using a mutex for thread safety.
- Server Message Sending: A message from the server to the client is sent for demonstration purposes.
Step 5: Cleanup and Program Termination
C
close(serverSocket); pthread_mutex_destroy(&mutex); return 0; |
- Socket Closure: The server socket is closed using close.
- Mutex Destruction: The mutex is destroyed using pthread_mutex_destroy.
- Program Termination: The program returns 0 to indicate successful termination.
Step 6: Write the Complete Code
Now, write the complete code on the server.c file.
C
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> #include <unistd.h> #define PORT 8888 #define MAX_MESSAGE_SIZE 1024 #define MAX_CLIENTS 10 typedef struct { struct sockaddr_in address; int socket; int id; } Client; Client clients[MAX_CLIENTS]; pthread_t threads[MAX_CLIENTS]; int clientCount = 0; pthread_mutex_t mutex; void *handleClient( void *arg) { int id = *(( int *)arg); char buffer[MAX_MESSAGE_SIZE]; ssize_t bytesRead; while (1) { // Receive message from client bytesRead = recvfrom(clients[id].socket, buffer, sizeof (buffer), 0, NULL, NULL); if (bytesRead == -1) { perror ( "Receive failed" ); exit (EXIT_FAILURE); } buffer[bytesRead] = '\0' ; printf ( "Client %d: %s" , id + 1, buffer); // Broadcast the received message to all clients pthread_mutex_lock(&mutex); for ( int i = 0; i < clientCount; ++i) { if (i != id) { sendto(clients[i].socket, buffer, bytesRead, 0, ( struct sockaddr*)&clients[i].address, sizeof (clients[i].address)); } } pthread_mutex_unlock(&mutex); // Send a message to the client (for demonstration purposes) sprintf (buffer, "Server: Message from server to Client %d\n" , id + 1); sendto(clients[id].socket, buffer, strlen (buffer), 0, ( struct sockaddr*)&clients[id].address, sizeof (clients[id].address)); } } int main() { int serverSocket; struct sockaddr_in serverAddr; char buffer[MAX_MESSAGE_SIZE]; socklen_t clientLen = sizeof ( struct sockaddr_in); ssize_t bytesRead; // Create socket if ((serverSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror ( "Socket creation failed" ); exit (EXIT_FAILURE); } // Configure server address memset (&serverAddr, 0, sizeof (serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(PORT); // Bind socket if (bind(serverSocket, ( struct sockaddr*)&serverAddr, sizeof (serverAddr)) == -1) { perror ( "Bind failed" ); exit (EXIT_FAILURE); } // Initialize mutex pthread_mutex_init(&mutex, NULL); printf ( "Server listening on port %d...\n" , PORT); // Create a thread for each client while (1) { // Receive message from client bytesRead = recvfrom(serverSocket, buffer, sizeof (buffer), 0, ( struct sockaddr*)&clients[clientCount].address, &clientLen); if (bytesRead == -1) { perror ( "Receive failed" ); exit (EXIT_FAILURE); } buffer[bytesRead] = '\0' ; printf ( "Client %d connected: %s" , clientCount + 1, buffer); // Add client to the list clients[clientCount].socket = serverSocket; clients[clientCount].id = clientCount; pthread_create(&threads[clientCount], NULL, handleClient, ( void *)&clients[clientCount].id); clientCount++; // Send a welcome message to the client (for demonstration purposes) sprintf (buffer, "Server: Welcome, you are Client %d\n" , clientCount); sendto(clients[clientCount - 1].socket, buffer, strlen (buffer), 0, ( struct sockaddr*)&clients[clientCount - 1].address, sizeof (clients[clientCount - 1].address)); } close(serverSocket); pthread_mutex_destroy(&mutex); return 0; } |
How to create a multi-chat server using UDP?
In this article, we will see the development of a Multi-Threaded UDP Chat Server-Client Network for Data Transfer in Linux. UDP is used for low latency and connectionless characteristics, the architecture consists of a server managing multiple clients through threading. This networked chat application facilitates real-time communication, allowing users to exchange messages seamlessly. We will create the Server and Client script and perform live execution of communication between the server and multiple clients.