Design a concurrent server for handling multiple clients using fork()
Last Updated :
25 Feb, 2022
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 concurrent clients and runs each call block in its own process control block (PCB).
Need for designing a concurrent server for handling clients using fork() call:
Through TCP basic server-client model, one server attends only one client at a particular time.
But, we are now trying to make our TCP server handle more than one client. Although, we can achieve this using select() system call but we can ease the whole process.
How is the fork() system call going to help in this?
Fork() creates a new child process that runs in sync with its Parent process and returns 0 if child process is created successfully.
- Whenever a new client will attempt to connect to the TCP server, we will create a new Child Process that is going to run in parallel with other clients' execution. In this way, we are going to design a concurrent server without using the Select() system call.
- A pid_t (Process id) data type will be used to hold the Child's process id. Example: pid_t = fork( ).
Difference from the other approaches:
This is the simplest technique for creating a concurrent server. Whenever a new client connects to the server, a fork() call is executed making a new child process for each new client.
- Multi-Threading achieves a concurrent server using a single processed program. Sharing of data/files with connections is usually slower with a fork() than with threads.
- Select() system call doesn't create multiple processes. Instead, it helps in multiplexing all the clients on a single program and doesn't need non-blocking IO.
Program to design a concurrent server for handling multiple clients using fork()
- Accepting a client makes a new child process that runs concurrently with other clients and the parent process
C
// Accept connection request from client in cliAddr
// socket structure
clientSocket = accept(
sockfd, (struct sockaddr*)&cliAddr, &addr_size);
// Make a child process by fork() and check if child
// process is created successfully
if ((childpid = fork()) == 0) {
// Send a confirmation message to the client for
// successful connection
send(clientSocket, "hi client", strlen("hi client"),
0);
}
C
// Server side program that sends
// a 'hi client' message
// to every client concurrently
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// PORT number
#define PORT 4444
int main()
{
// Server socket id
int sockfd, ret;
// Server socket address structures
struct sockaddr_in serverAddr;
// Client socket id
int clientSocket;
// Client socket address structures
struct sockaddr_in cliAddr;
// Stores byte size of server socket address
socklen_t addr_size;
// Child process id
pid_t childpid;
// Creates a TCP socket id from IPV4 family
sockfd = socket(AF_INET, SOCK_STREAM, 0);
// Error handling if socket id is not valid
if (sockfd < 0) {
printf("Error in connection.\n");
exit(1);
}
printf("Server Socket is created.\n");
// Initializing address structure with NULL
memset(&serverAddr, '\0',
sizeof(serverAddr));
// Assign port number and IP address
// to the socket created
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
// 127.0.0.1 is a loopback address
serverAddr.sin_addr.s_addr
= inet_addr("127.0.0.1");
// Binding the socket id with
// the socket structure
ret = bind(sockfd,
(struct sockaddr*)&serverAddr,
sizeof(serverAddr));
// Error handling
if (ret < 0) {
printf("Error in binding.\n");
exit(1);
}
// Listening for connections (upto 10)
if (listen(sockfd, 10) == 0) {
printf("Listening...\n\n");
}
int cnt = 0;
while (1) {
// Accept clients and
// store their information in cliAddr
clientSocket = accept(
sockfd, (struct sockaddr*)&cliAddr,
&addr_size);
// Error handling
if (clientSocket < 0) {
exit(1);
}
// Displaying information of
// connected client
printf("Connection accepted from %s:%d\n",
inet_ntoa(cliAddr.sin_addr),
ntohs(cliAddr.sin_port));
// Print number of clients
// connected till now
printf("Clients connected: %d\n\n",
++cnt);
// Creates a child process
if ((childpid = fork()) == 0) {
// Closing the server socket id
close(sockfd);
// Send a confirmation message
// to the client
send(clientSocket, "hi client",
strlen("hi client"), 0);
}
}
// Close the client socket id
close(clientSocket);
return 0;
}
C
// Client Side program to test
// the TCP server that returns
// a 'hi client' message
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// PORT number
#define PORT 4444
int main()
{
// Socket id
int clientSocket, ret;
// Client socket structure
struct sockaddr_in cliAddr;
// char array to store incoming message
char buffer[1024];
// Creating socket id
clientSocket = socket(AF_INET,
SOCK_STREAM, 0);
if (clientSocket < 0) {
printf("Error in connection.\n");
exit(1);
}
printf("Client Socket is created.\n");
// Initializing socket structure with NULL
memset(&cliAddr, '\0', sizeof(cliAddr));
// Initializing buffer array with NULL
memset(buffer, '\0', sizeof(buffer));
// Assigning port number and IP address
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
// 127.0.0.1 is Loopback IP
serverAddr.sin_addr.s_addr
= inet_addr("127.0.0.1");
// connect() to connect to the server
ret = connect(clientSocket,
(struct sockaddr*)&serverAddr,
sizeof(serverAddr));
if (ret < 0) {
printf("Error in connection.\n");
exit(1);
}
printf("Connected to Server.\n");
while (1) {
// recv() receives the message
// from server and stores in buffer
if (recv(clientSocket, buffer, 1024, 0)
< 0) {
printf("Error in receiving data.\n");
}
// Printing the message on screen
else {
printf("Server: %s\n", buffer);
bzero(buffer, sizeof(buffer));
}
}
return 0;
}
Compile script:
- Executing server side code
⇒ gcc server.c -o ser
./ser
- Executing client side code
⇒ gcc client.c -o cli
./cli
Output:
Advantages: The advantages of using this process are:
- Easy to implement in a program doing a far more complex task.
- Each child process (client) runs independently and is unable to read/write other clients' data.
- The server behaves as it has only one client connected to it. The child processes need not care about other incoming connections or the running of parallel child processes. Therefore, programming with fork() system call is transparent and takes less effort.
Disadvantages: The disadvantages are as mentioned here
- Fork is less efficient than multi-threading because it creates a large overhead by creating a new process but a thread is a lightweight process that shares the resources from the parent process itself.
- Operating system will need memory sharing or synchronization cost for achieving concurrency.
Similar Reads
Handling multiple clients on server with multithreading using Socket Programming in C/C++
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
6 min read
Create n-child process from same parent process using fork() in C
fork() is a system call function which can generate child process from parent main process. Using some conditions we can generate as many child process as needed. We have given n , we have to create n-child processes from same parent process (main process ). Examples: Input :3 Output :[son] pid 2533
2 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
Chain processes vs Fan of processes using fork() function in C
Fork System Call: The fork system call is used for creating a new process, which is called the child process, which runs concurrently with the process that makes the fork() call (parent process). After a new child process is created, both processes will execute the next instruction following the for
4 min read
Using fork() to produce 1 parent and its 3 child processes
Creating processes and managing their execution sequence is a fundamental aspect of operating systems and concurrent programming. In this article, we'll explore how to create a program that spawns four processes: one parent and three children, where each child terminates in a specific sequence. In t
6 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
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
Calculate server loads using Round Robin Scheduling
Given M servers that handle multiple requests having infinite computational capability and arrays arrivalTime[] and processTime[] of size N denoting the arrival time and load time of N requests in the following manner: Each server is numbered from 0 to (M - 1) and the requests are given in strictly
14 min read
UDP Client Server using connect | C implementation
UDP is a connection less protocol. There is no connection is established between client and server. Creating Standard UDP Client/Server is discussed here Prerequisite : Socket Programming in C/CPP | UDP server-client implementation In UDP, the client does not form a connection with the server like i
3 min read
Client Server Communication in Operating System
In an Operating System, Client Server Communication refers to the exchange of data and Services among multiple machines or processes. In Client client-server communication System one process or machine acts as a client requesting a service or data, and Another machine or process acts like a server f
3 min read