Handling multiple clients on server with multithreading using Socket Programming in C/C++
Last Updated :
20 Jul, 2022
This tutorial assumes that the reader has a basic knowledge of socket programming, i.e has a familiarity with basic server and client models. In the basic model, the server handles only one client at a time, which is a big assumption if one wants to develop any scalable server model. The simple way to handle multiple clients would be to spawn a new thread for every new client connected to the server.
Semaphores: Semaphore is simply a variable that is non-negative and shared between threads. This variable is used to solve the critical section problem and to achieve process synchronization in the multiprocessing environment.
sem_post: sem_post() increments (unlocks) the semaphore pointed to by sem. If the semaphore's value consequently becomes greater than zero, then another process or thread blocked in a sem_wait(3) call will be woken up and proceed to lock the semaphore.
#include <semaphore.h>
int sem_post(sem_t *sem);
sem_wait: sem_wait() decrements (locks) the semaphore pointed to by sem. If the semaphore's value is greater than zero, then the decrement proceeds and the function returns, immediately. If the semaphore currently has the value zero, then the call blocks until either it becomes possible to perform the decrement (i.e., the semaphore value rises above zero), or a signal handler interrupts the call.
#include <semaphore.h>
int sem_wait(sem_t *sem);
In this article, the Reader-Writers algorithm is implemented on the server-side.
Implementation: For the server-side, create two different threads; a reader thread, and a writer thread. First, declare a serverSocket, an integer, a variable to hold the return of socket function.
int serverSocket = socket(domain, type, protocol);
- serverSocket: Socket descriptor, an integer (like a file-handle).
- domain: Integer, communication domain e.g., AF_INET (IPv4 protocol), AF_INET6 (IPv6 protocol).
- type: Communication type.
- SOCK_STREAM: TCP(reliable, connection-oriented).
- SOCK_DGRAM: UDP(unreliable, connectionless).
- protocol: Protocol value for Internet Protocol(IP), which is 0. This is the same number that appears on the protocol field in the IP header of a packet.(man protocols for more details).
Then, after initializing all the necessary variables bind the socket.
bind: After the creation of the socket, the bind function binds the socket to the address and port number specified in addr(custom data structure). In the example code, we bind the server to the local host, hence INADDR_ANY is used to specify the IP address.
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
listen: It puts the server socket in a passive mode, where it waits for the client to approach the server to make a connection. The backlog defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED.
int listen(int sockfd, int backlog);
For more connection functions used in this article, please refer to this article for socket programming in C.
Approach:
- After accepting the connection to the desired port, receive an integer from the client that defines the choice for reading or writing. Choice 1 indicates reader, while choice 2 indicates writer.
- After successfully receiving data, call for pthread_create to create reader threads and writer threads.
- After making successful connections to the server-client asks the user for input on the choice variable.
- After getting the choice from the user, the client then sends this choice to the server to call the reader or writer thread by creating a client thread for the request.
Below is the implementation of the above approach:
Code for Server Side:
C
// C program for the Server Side
// inet_addr
#include <arpa/inet.h>
// For threading, link with lpthread
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
// Semaphore variables
sem_t x, y;
pthread_t tid;
pthread_t writerthreads[100];
pthread_t readerthreads[100];
int readercount = 0;
// Reader Function
void* reader(void* param)
{
// Lock the semaphore
sem_wait(&x);
readercount++;
if (readercount == 1)
sem_wait(&y);
// Unlock the semaphore
sem_post(&x);
printf("\n%d reader is inside",
readercount);
sleep(5);
// Lock the semaphore
sem_wait(&x);
readercount--;
if (readercount == 0) {
sem_post(&y);
}
// Lock the semaphore
sem_post(&x);
printf("\n%d Reader is leaving",
readercount + 1);
pthread_exit(NULL);
}
// Writer Function
void* writer(void* param)
{
printf("\nWriter is trying to enter");
// Lock the semaphore
sem_wait(&y);
printf("\nWriter has entered");
// Unlock the semaphore
sem_post(&y);
printf("\nWriter is leaving");
pthread_exit(NULL);
}
// Driver Code
int main()
{
// Initialize variables
int serverSocket, newSocket;
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
sem_init(&x, 0, 1);
sem_init(&y, 0, 1);
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8989);
// Bind the socket to the
// address and port number.
bind(serverSocket,
(struct sockaddr*)&serverAddr,
sizeof(serverAddr));
// Listen on the socket,
// with 40 max connection
// requests queued
if (listen(serverSocket, 50) == 0)
printf("Listening\n");
else
printf("Error\n");
// Array for thread
pthread_t tid[60];
int i = 0;
while (1) {
addr_size = sizeof(serverStorage);
// Extract the first
// connection in the queue
newSocket = accept(serverSocket,
(struct sockaddr*)&serverStorage,
&addr_size);
int choice = 0;
recv(newSocket,
&choice, sizeof(choice), 0);
if (choice == 1) {
// Creater readers thread
if (pthread_create(&readerthreads[i++], NULL,
reader, &newSocket)
!= 0)
// Error in creating thread
printf("Failed to create thread\n");
}
else if (choice == 2) {
// Create writers thread
if (pthread_create(&writerthreads[i++], NULL,
writer, &newSocket)
!= 0)
// Error in creating thread
printf("Failed to create thread\n");
}
if (i >= 50) {
// Update i
i = 0;
while (i < 50) {
// Suspend execution of
// the calling thread
// until the target
// thread terminates
pthread_join(writerthreads[i++],
NULL);
pthread_join(readerthreads[i++],
NULL);
}
// Update i
i = 0;
}
}
return 0;
}
Code for Client Side:
C
// C program for the Client Side
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
// inet_addr
#include <arpa/inet.h>
#include <unistd.h>
// For threading, link with lpthread
#include <pthread.h>
#include <semaphore.h>
// Function to send data to
// server socket.
void* clienthread(void* args)
{
int client_request = *((int*)args);
int network_socket;
// Create a stream socket
network_socket = socket(AF_INET,
SOCK_STREAM, 0);
// Initialise port number and address
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(8989);
// Initiate a socket connection
int connection_status = connect(network_socket,
(struct sockaddr*)&server_address,
sizeof(server_address));
// Check for connection error
if (connection_status < 0) {
puts("Error\n");
return 0;
}
printf("Connection established\n");
// Send data to the socket
send(network_socket, &client_request,
sizeof(client_request), 0);
// Close the connection
close(network_socket);
pthread_exit(NULL);
return 0;
}
// Driver Code
int main()
{
printf("1. Read\n");
printf("2. Write\n");
// Input
int choice;
scanf("%d", &choice);
pthread_t tid;
// Create connection
// depending on the input
switch (choice) {
case 1: {
int client_request = 1;
// Create thread
pthread_create(&tid, NULL,
clienthread,
&client_request);
sleep(20);
break;
}
case 2: {
int client_request = 2;
// Create thread
pthread_create(&tid, NULL,
clienthread,
&client_request);
sleep(20);
break;
}
default:
printf("Invalid Input\n");
break;
}
// Suspend execution of
// calling thread
pthread_join(tid, NULL);
}
Output:

Time Complexity: O(1)
Auxiliary Space: O(1)
Similar Reads
TCP Client-Server Program to Check if a Given String is Palindrome
Prerequisites: Socket Programming in C/C++, TCP and UDP server using select, UDP Server-Client implementation in C TCP Client-Server Implementation in C This article describes a Client and Server setup where a Client connects, sends a string to the server and the server shows the original string and
4 min read
UDP Server-Client implementation in C++
There are two primary transport layer protocols to communicate between hosts: TCP and UDP. Creating TCP Server/Client was discussed in a previous post. Prerequisite: Creating TCP Server/Client Theory: In UDP, the client does not form a connection with the server like in TCP and instead sends a datag
4 min read
Design a concurrent server for handling multiple clients using fork()
Prerequisite: Socket Programming in C/C++, fork() System call Problem Statement: In this article, we are going to write a program that illustrates the Client-Server Model using fork() system call which can handle multiple clients concurrently. Fork() call creates multiple child processes for concu
6 min read
C program for file Transfer using UDP
Data can be transferred between two computers using Socket programming in C. Similarly, files can easily be sent using UDP protocol and a simple client/server. Security: Handled by encryption. Protocol : UDP Encryption: XOR encryption Algorithm : 1. The server starts and waits for filename. 2. The c
4 min read
C Program to Show Thread Interface and Memory Consistency Errors
Thread interface and Memory consistency errors are demonstrated in this program. Threads are a way for a program to split itself into two or more concurrently running tasks. This means that a program can perform multiple operations at the same time, rather than having to execute them one after the o
10 min read
Parallel Programming in C
Parallel programming is a technique that allows multiple computations to be performed simultaneously, taking advantage of multi-core processors and distributed computing systems. Parallel programming can improve the system's performance by dividing the bigger task into smaller chunks and executing t
5 min read
Producer Consumer Problem in C
Concurrency is an important topic in concurrent programming since it allows us to completely understand how the systems work. Among the several challenges faced by practitioners working with these systems, there is a major synchronization issue which is the producer-consumer problem. In this article
5 min read
How to use make utility to build C projects?`
When we build projects in C/C++, we have dependencies among files. For example, there might be a file a.c that calls a function from b.c. So we must compile b.c before a.c. There might be many dependencies in a project and manually following these dependencies and compiling files one by one becomes
5 min read
OpenMP | Hello World program
Prerequisite: OpenMP | Introduction with Installation GuideIn C/C++/Fortran, parallel programming can be achieved using OpenMP. In this article, we will learn how to create a parallel Hello World Program using OpenMP.STEPS TO CREATE A PARALLEL PROGRAMÂ Â Include the header file: We have to include th
3 min read
Socket Programming in C/C++: Handling multiple clients on server without multi threading
This tutorial assumes you have a basic knowledge of socket programming, i.e you are familiar with basic server and client model. In the basic model, server handles only one client at a time, which is a big assumption if you want to develop any scalable server model. The simple way to handle multiple
9 min read