How to create a multi-chat server using UDP?
Last Updated :
26 Dec, 2023
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.
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
Creating server.c file
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;
}
Server Script Code
Creating Client Script
In this section, we will create the Client script through which multiple clients can connect to the server for data transfer and message transfer.
Step 1: Create a client.c File
Firstly, create the client.c file in the nano editor by using the command in the terminal.
nano client.c
Creating client.c file
Step 2: Client Initialization and Receive Thread
C
typedef struct {
struct sockaddr_in serverAddr;
int socket;
} Client;
Client client;
void *receiveMessages(void *arg) {
char buffer[MAX_MESSAGE_SIZE];
ssize_t bytesRead;
while (1) {
bytesRead = recvfrom(client.socket, buffer, sizeof(buffer), 0, NULL, NULL);
if (bytesRead == -1) {
perror("Receive failed");
exit(EXIT_FAILURE);
}
buffer[bytesRead] = '\0';
printf("Server: %s", buffer);
}
}
- Client Structure Definition: A structure (Client) is defined to hold information about the server address and the client socket.
- Client Initialization: An instance of the Client structure (client) is created to store client-related information.
- Receive Thread Function: The receiveMessages function is defined to handle continuous message reception from the server in a separate thread.
Step 3: Socket Creation and Message Sending
C
int main() {
pthread_t receiveThread;
char message[MAX_MESSAGE_SIZE];
ssize_t bytesRead;
// Create socket
if ((client.socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// Configure server address
memset(&client.serverAddr, 0, sizeof(client.serverAddr));
client.serverAddr.sin_family = AF_INET;
client.serverAddr.sin_port = htons(PORT);
if (inet_aton("127.0.0.1", &client.serverAddr.sin_addr) == 0) {
perror("Invalid server address");
exit(EXIT_FAILURE);
}
// Create a thread to receive messages
pthread_create(&receiveThread, NULL, receiveMessages, NULL);
// Send and receive messages
while (1) {
// Get user input
printf("Enter message: ");
fgets(message, sizeof(message), stdin);
// Send message to server
sendto(client.socket, message, strlen(message), 0, (struct sockaddr*)&client.serverAddr, sizeof(client.serverAddr));
}
close(client.socket);
return 0;
}
- Socket Creation: A UDP socket is created using the socket system call.
- Server Address Configuration: The server address is configured with the server's IP, port, and protocol details.
- Thread Creation: A thread (receiveThread) is created to run the receiveMessages function concurrently.
- Message Sending Loop: Inside an infinite loop, the client continuously prompts the user for input, sends the message to the server using sendto, and then repeats the process.
- Socket Closure: When the program terminates, the client socket is closed using close.
Step 4: Receive Thread Function
C
void *receiveMessages(void *arg) {
char buffer[MAX_MESSAGE_SIZE];
ssize_t bytesRead;
while (1) {
bytesRead = recvfrom(client.socket, buffer, sizeof(buffer), 0, NULL, NULL);
if (bytesRead == -1) {
perror("Receive failed");
exit(EXIT_FAILURE);
}
buffer[bytesRead] = '\0';
printf("Server: %s", buffer);
}
}
- Thread Function Definition: The receiveMessages function continuously receives messages from the server using recvfrom.
- Buffer Initialization: A buffer (buffer) is used to store the received message.
- Error Handling: If the reception fails, an error message is printed, and the program exits with an error status.
- Message Printing: The received message is printed to the console with a prefix indicating it's from the server.
Step 5: Write the Entire Code
Now, write the entire code in the client.c which is stated below in the code block.
C
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PORT 8888
#define MAX_MESSAGE_SIZE 1024
typedef struct {
struct sockaddr_in serverAddr;
int socket;
} Client;
Client client;
void* receiveMessages(void* arg)
{
char buffer[MAX_MESSAGE_SIZE];
ssize_t bytesRead;
while (1) {
bytesRead = recvfrom(client.socket, buffer,
sizeof(buffer), 0, NULL, NULL);
if (bytesRead == -1) {
perror("Receive failed");
exit(EXIT_FAILURE);
}
buffer[bytesRead] = '\0';
printf("Server: %s", buffer);
}
}
int main()
{
pthread_t receiveThread;
char message[MAX_MESSAGE_SIZE];
ssize_t bytesRead;
// Create socket
if ((client.socket = socket(AF_INET, SOCK_DGRAM, 0))
== -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// Configure server address
memset(&client.serverAddr, 0,
sizeof(client.serverAddr));
client.serverAddr.sin_family = AF_INET;
client.serverAddr.sin_port = htons(PORT);
if (inet_aton("127.0.0.1", &client.serverAddr.sin_addr)
== 0) {
perror("Invalid server address");
exit(EXIT_FAILURE);
}
// Create a thread to receive messages
pthread_create(&receiveThread, NULL, receiveMessages,
NULL);
// Send and receive messages
while (1) {
// Get user input
printf("Enter message: ");
fgets(message, sizeof(message), stdin);
// Send message to server
sendto(client.socket, message, strlen(message), 0,
(struct sockaddr*)&client.serverAddr,
sizeof(client.serverAddr));
}
close(client.socket);
return 0;
}
Client Script Code
Steps to Execute Scripts
Step 1: Compile Server and Client Scripts
Once we have created the scripts, we need to compile them with the gcc compiler. So execute the below commands in the terminal to compile it successfully:
gcc server.c -o server -lpthread
gcc client.c -o client -lpthread
Compiling Scripts
Step 2: Data Exchange between Server-Client
In the below video output, we can see that we have executed server and multiple client scripts.
Output
- The message sent from each client is been managed by the server giving it responsibility.
- The server handles multiple clients in thread form.
- Here, UDP broadcasting broadcasts messages in a multi-client environment, which ensures a proper communication process.
- Due to UDP broadcast, the messages are broadcasted among all the machines including the clients, the message sent by the client is also broadcasted to other clients including the server, and vice versa.
Conclusion
In conclusion, the provided C code establishes a simple UDP chat client capable of sending and receiving messages to and from a server. The program utilizes multithreading, with a dedicated thread for continuously receiving messages from the server while the main thread handles user input and message transmission. The client initiates a UDP socket, configures the server address, and creates a separate thread for message reception.
Similar Reads
How to Create a Chat App Using socket.io in NodeJS?
Socket.io is a JavaScript library that enables real-time, bidirectional, event-based communication between the client and server. It works on top of WebSocket but provides additional features like automatic reconnection, broadcasting, and fallback options. What We Are Going to Create?In this article
5 min read
How to Create and Set Up a Stage Channel in Discord
Ever wondered how large Discord communities host smooth, organized voice events without everyone talking over each other? Thatâs exactly what Stage Channels in Discord are made for. They allow a few selected speakers to talk while others listen, making them ideal for AMAs, announcements, podcasts, a
8 min read
How to Create a Group Chat in Discord
How to Start a Group Chat on Discord- Quick StepsOpen Discord App or Web AppGo to the Friends TabClick on '+' create DM IconSelect Friends > Click Start DMStart Group ChatDiscord has emerged as a popular platform that offers a variety of features to bring communities and friends together. One of
7 min read
How to Create a Non-Blocking Server in Java?
A Non-Blocking server means that it is able to have multiple requests in progress at the same time by the same process or thread because it uses Non-Blocking I/O. In the Non-Blocking approach - one thread can handle multiple queries at a time. A server using a Non-Blocking socket works in an asynchr
7 min read
Create ChatGPT Template using HTML CSS & JavaScript
Create a customizable ChatGPT interface using HTML, CSS, and JavaScript. Implement features like message input, display area, and toggle buttons for themes. Customize styles and interactions to suit your preferences and requirements. Prerequisites:HTMLCSSJavaScriptApproachCreate a new HTML file and
7 min read
How to Make a Discord Server Public
Discord, initially popular among gamers, has expanded into a versatile platform that hosts a variety of interest groups, from educational clubs to hobbyist communities. Making your server public allows you to invite more participants, fostering diverse discussions and broadening the reach of your co
7 min read
How to build a chatbot using ChatGPT?
A chatbot is a computer program designed to simulate human conversation, usually through text or voice interactions. They use natural language processing (NLP) and machine learning algorithms to understand and respond to user queries, providing a personalized experience. Chatbots can be used for a w
6 min read
How to Create Web Server On Packet Tracer?
A web server is like a computer that uses an HTTP (Hyper Text Transfer Protocol) and many other protocols. it responds when a client makes a request over the World Wide Web. The main work of the web server is to show website content that is processed, and stored, in the webserver to deliver the webp
2 min read
How to Create Your Own ChatGPT Plugin?
Plugin is a software extension that brings in additional functionalities to an already existing application, especially in cases where implementing additional functionalities in the base code will be very complex and time-consuming. Plugins are one of the best ways to use services beyond limitations
12 min read
How to Use Discordâs Screen Share and Video Call Features
Use the power of Discord with screen sharing and video calls! Upgrade your communication with clear video chat and easily share your screen for presentations, collaborative work, or just hanging out with friends. This guide unlocks the secrets to mastering these features on both desktop and mobile.
5 min read