Lab 02 - NP - Aashutosh Poudel
Lab 02 - NP - Aashutosh Poudel
Checked by:
....................................
TABLE OF CONTENTS
TABLE OF CONTENTS........................................................................................................2
1. INTRODUCTION............................................................................................................3
2. OBJECTIVES..................................................................................................................3
3. IMPLEMENTATION......................................................................................................3
4.1 Implementation of concurrent Server with fork() in Unix........................................3
4.2 Implementation of handling multiple descriptors using select() system call..........8
4. RESULT.........................................................................................................................13
5. CONCLUSION..............................................................................................................14
APPENDICES.......................................................................................................................16
2
1. INTRODUCTION
In modern computer networks, server applications are expected to handle multiple client
connections efficiently and concurrently. Two fundamental approaches in Unix systems for
achieving concurrency in network programming are process forking and I/O multiplexing
using system calls such as select(). These techniques empower server programs to serve
multiple clients without significant delays or resource contention.
The fork() system call allows a server to create a new process for each client connection.
Each child process handles one client independently, enabling parallel client-server
interactions. While simple and effective, this model can consume more system resources
due to process overhead.
Alternatively, the select() system call facilitates concurrent client handling within a single
process. It monitors multiple file descriptors (sockets) to detect which ones are ready for
reading or writing, allowing the server to respond to multiple clients without spawning
separate processes. This approach is more resource-efficient and suitable for servers
expected to manage a large number of simultaneous connections.
2. OBJECTIVES
1. To implement a concurrent TCP server using the fork () system call to handle
multiple client connections in parallel.
2. To develop a TCP server capable of managing multiple client sockets using the
select () system call within a single process.
3. IMPLEMENTATION
Fork_Client Code
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include
<sys/socket.h>
#include <unistd.h>
#define PORT 8080
int main(int argc, char const *argv[])
3
{
int status, valread,
client_fd; struct sockaddr_in
serv_addr; char *hello =
"Hello from client"; char
buffer[1024] = {0};
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \
n"); return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf(
"\nInvalid address/ Address not supported \
n"); return -1;
}
if ((status = connect(client_fd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr))) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
send(client_fd, hello, strlen(hello),
0); printf("Hello message sent\n");
valread = read(client_fd, buffer,
1024); printf("%s\n", buffer);
return 0;
}
4
char buffer[1024] = {0};
— Initializes a buffer to receive the server's response.
serv_addr.sin_family = AF_INET;
— Sets address family to IPv4.
serv_addr.sin_port = htons(PORT);
— Converts port number to network byte order.
printf("%s", buffer);
— Displays server's response.
return 0;
— Ends the program successfully.
Fork_Server Code
#include
<netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include
<sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#define PORT 8080
void handle_client(int new_socket)
{
char buffer[1024] = {0};
char *hello = "Hello from
server."; int valread;
valread = read(new_socket, buffer, 1024);
5
send(new_socket, hello, strlen(hello),
0); printf("Hello message sent to
client\n"); close(new_socket);
exit(0);
}
int main()
{
int server_fd,
new_socket; struct
sockaddr_in address; int
opt = 1;
int addrlen = sizeof(address);
signal(SIGCHLD, SIG_IGN);
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 5) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n",
PORT); while (1)
{
if ((new_socket = accept(server_fd, (struct sockaddr
*)&address, (so cklen_t * )&addrlen)) < 0)
{
perror("accept");
continue;
}
pid_t pid =
fork(); if (pid ==
0)
{
close(server_fd);
handle_client(new_socket);
}
else if (pid > 0)
{
close(new_socket);
}
else
{
6
perror("fork failed");
close(new_socket);
}
}
close(server_fd);
return 0;
}
#define PORT 8080 — Defines the port number server listens on.
7
address.sin_port = htons(PORT); — Converts port to network byte order.
Select_Client Code:
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include
<sys/socket.h>
#include <unistd.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int status, valread,
client_fd; struct sockaddr_in
serv_addr; char *hello =
"Hello from client"; char
buffer[1024] = {0};
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \
n"); return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf(
"\nInvalid address/ Address not supported \
n"); return -1;
}
if ((status = connect(client_fd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr))) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
8
printf("Hello message sent\n");
valread = read(client_fd, buffer,
1024); printf("%s\n", buffer);
return 0;
}
serv_addr.sin_family = AF_INET;
— Sets address family to IPv4.
serv_addr.sin_port = htons(PORT);
— Converts port number to network byte order.
9
printf("Hello message sent ");
— Prints confirmation of sent message.
printf("%s", buffer);
— Displays server's response.
return 0;
— Ends the program successfully.
Select_Server Code:
#include
<netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include
<sys/socket.h>
#include
<sys/select.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#define PORT 8080
#define MAX_CLIENTS 30
int main()
{
int server_fd, new_socket, client_socket[MAX_CLIENTS];
struct sockaddr_in address;
int max_sd, sd, activity,
valread; int opt = 1;
int addrlen = sizeof(address);
char buffer[1025];
char *hello = "Hello from
server."; fd_set readfds;
for (int i = 0; i < MAX_CLIENTS; i++)
client_socket[i] = 0;
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
10
exit(EXIT_FAILURE);
}
if (listen(server_fd, 5) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n",
PORT); while (1)
{
FD_ZERO(&readfds);
FD_SET(server_fd,
&readfds); max_sd =
server_fd;
for (int i = 0; i < MAX_CLIENTS; i++)
{
sd =
client_socket[i]; if
(sd > 0)
FD_SET(sd,
&readfds); if (sd >
max_sd)
max_sd = sd;
}
activity = select(max_sd + 1, &readfds, NULL, NULL,
NULL); if ((activity < 0) && (errno != EINTR))
{
perror("select error");
}
if (FD_ISSET(server_fd, &readfds))
{
if ((new_socket = accept(server_fd, (struct sockaddr
*)&address,
(socklen_t *)&addrlen)) < 0)
{
perror("accept");
exit(EXIT_FAILURE);
}
printf("New connection: socket fd %d, IP: %s, port: %d\n",
new_socket, inet_ntoa(address.sin_addr),
n_port)) ntohs(address.si
;
for (int i = 0; i < MAX_CLIENTS; i++)
{
if (client_socket[i] == 0)
{
client_socket[i] = new_socket;
break;
}
}
}
for (int i = 0; i < MAX_CLIENTS; i++)
{
sd = client_socket[i];
if (FD_ISSET(sd, &readfds))
{
memset(buffer, 0,
sizeof(buffer)); valread =
11
read(sd, buffer, 1024); if
(valread == 0)
{
12
getpeername(sd, (struct sockaddr *)&address,
(socklen_t
*)&addrlen);
printf("Client disconnected: IP %s, port %d\n",
inet_ntoa(address.sin_addr),
ntohs(address.sin_po
rt));
close(sd);
client_socket[i] = 0;
}
else
{
printf("Message from client (fd %d): %s\n", sd,
buffer); send(sd, hello, strlen(hello), 0);
}
}
}
}
return 0;
Explanation of each line of Code:
13
Listen for incoming connections with listen().
Print listening message.
Infinite server loop:
Clear readfds, add server socket.
Add all active client sockets to readfds.
Track max socket descriptor.
Use select() to wait for activity on sockets.
On error, print message.
If new connection on server socket:
o Accept it with accept().
o Print new connection info (socket fd, IP, port).
o Add new socket to client array.
Loop through clients:
o If client socket ready, read data.
o If read returns 0, client disconnected:
Print disconnect message with IP and port.
Close socket, remove from array.
o Else print received message and send hello reply.
return 0; — End program.
4. RESULT
In the fork-based server implementation, the server uses the fork() system call to create a
new child process for each client connection. This allows multiple clients to be handled
concurrently, as each connection runs in its own process space. When a client sends a
message (e.g., "Hello from client"), the server receives it, prints it, and sends back a
predefined response. This demonstrates a functioning concurrent communication using
process-based concurrency.
Sample Output:
Server Terminal (fork_server.c):
14
New client connected.
Received from client: Hello from client
Sent: Hello from server.
Hello message
sent Hello from
server.
Sample Output:
Hello message
sent Hello from
server.
5. CONCLUSION
This lab activity provided practical experience in implementing concurrent server
architectures using two different approaches: fork() and select(). Through the fork()-
based model, we learned how to create multiple child processes to handle client
connections in parallel, gaining insights into process management and isolation. The
select()-based server introduced the concept of I/O multiplexing, allowing us to
handle multiple clients efficiently within a single process. These exercises deepened
our understanding of socket programming, concurrency, and system-level resource
15
management, which are essential for developing scalable and responsive network
applications.
16
APPENDICES
System Configuration
Compilation Commands
17
18