0% found this document useful (0 votes)
2 views18 pages

Lab 02 - NP - Aashutosh Poudel

The lab report details the implementation of network programming techniques in Unix, focusing on creating concurrent servers using fork() and select() system calls. It outlines the objectives, implementation steps, and provides code examples for both client and server applications. The report emphasizes the efficiency of handling multiple client connections in network applications.

Uploaded by

shariashrestha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

Lab 02 - NP - Aashutosh Poudel

The lab report details the implementation of network programming techniques in Unix, focusing on creating concurrent servers using fork() and select() system calls. It outlines the objectives, implementation steps, and provides code examples for both client and server applications. The report emphasizes the efficiency of handling multiple client connections in network applications.

Uploaded by

shariashrestha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 18

POKHARA UNIVERSITY

Faculty of Science and Technology


School of Engineering

Lab Report On: Network Programming (CMP 380)

Submitted By: Submitted To:


Name: Aashutosh Poudel Department of Software Engineering
Roll no: 03
Program: Software
Lab Sheet no: 02
Submission Date: 2082/02/25

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

4.1 Implementation of concurrent Server with fork() in Unix

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;
}

Explanation of each line of Code:

#include <arpa/inet.h>, <stdio.h>, <string.h>, <sys/socket.h>, <unistd.h> — For IP address


manipulation, I/O functions, string handling, socket operations, and system calls.

#define PORT 8080


— Defines the server's port number as 8080.

int main(int argc, char const *argv[])


— Main function with command-line arguments.

int status, valread, client_fd;


— Declares variables to hold status, read bytes, and socket descriptor.

struct sockaddr_in serv_addr;


— Structure to store server address info.

char *hello = "Hello from client";


— Message to be sent to the server.

4
char buffer[1024] = {0};
— Initializes a buffer to receive the server's response.

if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)


— Creates a TCP socket and checks for error.

serv_addr.sin_family = AF_INET;
— Sets address family to IPv4.

serv_addr.sin_port = htons(PORT);
— Converts port number to network byte order.

if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)


— Converts IP address from text to binary.

if ((status = connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0)


— Connects to the server and checks for failure.

send(client_fd, hello, strlen(hello), 0);


— Sends message to the server.

printf("Hello message sent ");


— Prints confirmation of sent message.

valread = read(client_fd, buffer, 1024);


— Reads server response into buffer.

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;
}

Explanation of each line of Code:

#include <netinet/in.h>, <stdio.h>, <stdlib.h>, <string.h>, <sys/socket.h>, <sys/types.h>,


<unistd.h>, <signal.h> — For socket functions, memory handling, process control, and
signal handling.

#define PORT 8080 — Defines the port number server listens on.

void handle_client(int new_socket) — Handles one client connection in a child process.

char buffer[1024] = {0}; — Buffer to store received message.

char *hello = "Hello from server."; — Server response message.

valread = read(new_socket, buffer, 1024); — Reads message from client socket.

send(new_socket, hello, strlen(hello), 0); — Sends response back to client.

close(new_socket); — Closes client socket.

exit(0); — Ends child process.

int main() — Entry point of server program.

int server_fd, new_socket; — Server and client socket descriptors.

struct sockaddr_in address; — Stores server address configuration.

int opt = 1; — Used to set socket options.

int addrlen = sizeof(address); — Length of address struct.

signal(SIGCHLD, SIG_IGN); — Ignores child termination to prevent zombies.

socket(AF_INET, SOCK_STREAM, 0) — Creates TCP socket.

setsockopt(...) — Allows reuse of address and port.

address.sin_family = AF_INET; — Sets address family to IPv4.

address.sin_addr.s_addr = INADDR_ANY; — Accept connections from any IP.

7
address.sin_port = htons(PORT); — Converts port to network byte order.

bind(...) — Binds socket to address.

listen(...) — Starts listening for client connections.

accept(...) — Accepts an incoming client connection.

fork() — Creates a new process for client handling.

close(server_fd); — Closes server socket (in child).

close(new_socket); — Closes client socket (in parent).

perror(...) — Prints error message.

4.2 Implementation of handling multiple descriptors using select()


system call.

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;
}

Explanation of each line of Code:

#include <arpa/inet.h>, <stdio.h>, <string.h>, <sys/socket.h>, <unistd.h> — For IP address


manipulation, I/O functions, string handling, socket operations, and system calls.

#define PORT 8080


— Defines the server's port number as 8080.

int main(int argc, char const *argv[])


— Main function with command-line arguments.

int status, valread, client_fd;


— Declares variables to hold status, read bytes, and socket descriptor.

struct sockaddr_in serv_addr;


— Structure to store server address info.

char *hello = "Hello from client";


— Message to be sent to the server.

char buffer[1024] = {0};


— Initializes a buffer to receive the server's response.

if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)


— Creates a TCP socket and checks for error.

serv_addr.sin_family = AF_INET;
— Sets address family to IPv4.

serv_addr.sin_port = htons(PORT);
— Converts port number to network byte order.

if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)


— Converts IP address from text to binary.

if ((status = connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0)


— Connects to the server and checks for failure.

send(client_fd, hello, strlen(hello), 0);


— Sends message to the server.

9
printf("Hello message sent ");
— Prints confirmation of sent message.

valread = read(client_fd, buffer, 1024);


— Reads server response into buffer.

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:

#include <netinet/in.h>, <stdio.h>, <stdlib.h>, <string.h>, <sys/socket.h>, <sys/select.h>,


<unistd.h>, <arpa/inet.h>, <errno.h> — For network programming, input/output, memory
management, socket functions, multiplexing with select(), system calls, IP address
conversion, and error handling.
#define PORT 8080 — Server port number.
#define MAX_CLIENTS 30 — Maximum simultaneous clients.
int server_fd, new_socket, client_socket[MAX_CLIENTS]; — Server socket, new client
socket, and array for client sockets.
struct sockaddr_in address; — Server address info struct.
int max_sd, sd, activity, valread; — Variables for max socket descriptor, current socket,
select activity, bytes read.
int opt = 1; — Option to reuse address.
int addrlen = sizeof(address); — Length of address struct.
char buffer[1025]; — Buffer to store client messages.
char *hello = "Hello from server."; — Server reply message.
fd_set readfds; — Set of sockets for select().
Initialize all client sockets to 0.
Create TCP socket with socket().
Set socket option to reuse address with setsockopt().
Configure address family, IP, and port.
Bind socket to IP and port with bind().

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

4.1 Output of the Fork Implementation


After successful compilation and execution:

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.

Client Terminal (client.c):

Hello message
sent Hello from
server.

4.2 Output of the Select Implementation


After successful compilation and execution:

In the select-based server implementation, the server handles multiple clients


simultaneously using I/O multiplexing. It monitors all socket descriptors using the select()
system call, and serves clients in a single-threaded, event-driven loop. This approach is
efficient in terms of memory and process overhead, and allows the server to respond to
multiple clients asynchronously.

Sample Output:

Server Terminal (select_server.c):

Server listening on port 8080...


New connection: socket fd 5, IP: 127.0.0.1, port: 52942
Message from client (fd 5): Hello from client
Client disconnected: IP 127.0.0.1, port: 52942

Client Terminal (select_client.c):

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

Appendix A: Compilation and Execution of TCP & UDP

System Configuration

 Operating System: MacOS


 Compiler: GCC (GNU Compiler Collection)
 Tools Used: Terminal, Text Editor (e.g., VS Code, Gedit)

Compilation Commands

 To compile Fork Server:

gcc server.c -o server


./server

 To compile Fork Client:

gcc client.c -o client


./client

 To compile Select Server:

gcc select-server.c -o select-server


./select-server

 To compile Select Client:

gcc select-client.c -o select-client


./select-client

17
18

You might also like