0% found this document useful (0 votes)
49 views

Expt 9 Chat

The document describes how to implement a multi-user chat server using TCP and the select system call. It provides code for a client and server that allow multiple clients to connect to the server and exchange messages in real-time by having the server monitor all connected sockets for incoming data.

Uploaded by

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

Expt 9 Chat

The document describes how to implement a multi-user chat server using TCP and the select system call. It provides code for a client and server that allow multiple clients to connect to the server and exchange messages in real-time by having the server monitor all connected sockets for incoming data.

Uploaded by

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

Experiment 9

Implementation of a multi user chat server using TCP as transport


layer protocol

Aim: To implement a chat server so that multiple users can chat simultaneously

Description

To implement chat server the select function is used. It is a function called by a process. When a
process calls this function , the process goes to sleep and wakes up only when one or more events
occur or when a specified amount of time has passed.

int select (int maxfdp1, fd_set * readset, fd_set * writeset, fd_set * exceptset, const struct
timeval * timeout);

The header files required are <sys/select.h> and <sys/time.h>


The function returns -1 on error, count of ready descriptors and 0 on timeout. If we want the kernel
to wait for as long as one descriptor becomes ready then we specify the timeout argument as a null
pointer. readset, writeset and exceptset specify the descriptors that we want the kernel to test for
reading, writing and exception conditions. select uses descriptor sets. Each set is an array of
integers. Each bit in an integer corresponds to a descriptor. In a 32 bit integer, the first element of
the array represents descriptors from 0 to 31. Second element of the array represents descriptors
from 32 to 63 and so on. We have four macros for the fd_set data type.

void FD_ZERO(fd_set * fdset); // clears all bits in fd_set


void FD_SET(int fd, fd_set * fdset); // turns on the bit for fd in fdset
void FD_CLR(int fd, fd_set * fdset); // turns off the bit for fd in fdset
int FD_ISSET(int fd, fd_set * fdset); // is the bit for fd on in fdset

Usage:

fd_set rset;
FD_ZERO(&rset); // all bits off
FD_SET(1, &rset); // turn on bit for fd 1
FD_SET(4, &rset); // turn on bit for fd 4

Initially you have to initialize the set. If we are not interested in a condition we can set that
argument of select to NULL, i.e; for readset, writeset or exceptset.

maxfdp1 argument specifies the number of descriptors to be tested. It is equal to value of max
descriptor to be tested plus one. This is because descriptor starts with 0. When we call select we
specify the values of the descriptors that we are interested in and on return the result indicates
which descriptors are ready. We turn on all the bits in the descriptor sets that we are interested in.
On return of the call, descriptors that are not ready will have the corresponding bit cleared in the
descriptor set.

We can use select to create a server which can handle clients without forking process for each
client.
rset

fd0 fd1 fd2 fd3


0 0 0 1
Maxfd + 1 = 4

Client

-1
-1
-1
-1
.
.
.
-1

Descriptors 0, 1, 2 are respectively for standard input, output and error. Next available descriptor is
3 which is set for listening socket. Client is an array that contains the connected socket descriptor
for each client. All elements are initialized to -1. The first non zero for descriptor set is that for the
listening socket. When the first client establishes a connection with the server, the listening
descriptor becomes readable and server calls accept. The new connected descriptor will be 4. The
arrays are updated.

rset

fd0 fd1 fd2 fd3 fd4


0 0 0 1 1
Maxfd + 1 = 5

Client

4
-1
-1
-1
.
.
.
-1

Now if a second client connects


rset

fd0 fd1 fd2 fd3 fd4 fd5


0 0 0 1 1 1
Maxfd + 1 = 6

Client

4
5
-1
-1
.
.
.
-1

If the first client terminates connection by sending a FIN segment, descriptor 4 becomes readable
and read returns 0. This socket is closed by the server and data structures are updated.

rset

fd0 fd1 fd2 fd3 fd4 fd5


0 0 0 1 0 1
Maxfd + 1 = 6

Client

-1
5
-1
-1
.
.
.
-1

The descriptor 4 in rset is set to zero. When clients arrive the connected socket descriptor is placed
in the first available entry, i.e; first entry with value equal to -1.

Program
client

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<stdlib.h>

main(int argc, char * argv[])


{
int i,j,n;
int sock_fd, max_fd, nready, fd[2];

char buffer[100], line[100];

struct sockaddr_in servaddr;


fd_set rset;

if(argc != 3)
{
fprintf(stderr, "Usage: ./client IPaddress_of_server port\n");
exit(1);
}

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


{
printf("Cannot create socket\n");
exit(1);
}

bzero((char*)&servaddr, sizeof(servaddr));
bzero(line, sizeof(line));

servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

if(connect(sock_fd, (struct sockaddr *)&servaddr, sizeof(servaddr))< 0)


{
perror("Connection failed:");
exit(1);
}

fd[0] = 0;
fd[1] = sock_fd;
for(; ; )
{
FD_ZERO(&rset);
FD_SET(0, &rset);
FD_SET(sock_fd, &rset);
bzero(line, sizeof(line));

max_fd = sock_fd;

nready = select(max_fd + 1, &rset, NULL, NULL, NULL);

for ( j = 0; j <2 ; j++)


{

if(FD_ISSET(fd[j], &rset))
{

n = read(fd[j], line, sizeof(line));

if(j == 0)
{
n = write(fd[j+1], line, strlen(line));

}
else
{
printf("%s \n", line);
}

if(--nready == 0)
break;
}
}
}
}

server

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<stdlib.h>

main(int argc, char * argv[])

{
int n, i, maxi, max_fd, k;
int sock_fd, listen_fd, connfd, client_no;
int nready, num_q, client[100], chat[100], conn[1000];

char line[1000], buffer[1000];

fd_set rset, allset;

struct sockaddr_in servaddr, cliaddr;

int len = sizeof(cliaddr);


bzero(line, sizeof(line));

client_no = 0;

if(argc != 2)
{
fprintf(stderr, "Usage: ./server port\n");
exit(1);
}

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


{
printf("Cannot create socket\n");
exit(1);
}

bzero((char*)&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[1]));
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)


{
perror("bind failed:");
exit(1);
}

listen(listen_fd, num_q);
max_fd = listen_fd;

maxi = -1;

for(i=0; i < 100; i++)


{
client[i] = -1;
chat[i] = -1;
}

FD_ZERO(&allset);
FD_SET(listen_fd, &allset);

for(; ;)
{
rset = allset;
nready = select(max_fd + 1, &rset, NULL, NULL, NULL);

if(FD_ISSET(listen_fd, &rset))
{
if((connfd = accept(listen_fd, (struct sockaddr *) &cliaddr, &len))<0)
{
perror("accept failed");
exit(1);
}

chat[++client_no] = connfd;
conn[connfd] = client_no;

k = client_no;
sprintf(buffer, "Client %d has joined chat\n", k);

while(k > 0 )
{
if(chat[k] >0)
n = write(chat[k--], buffer, strlen(buffer));
}

for( i = 0; i < 100; i++)


{
if( client[i] < 0)
{
client[i] = connfd;
break;
}

FD_SET(connfd, &allset);
if(connfd > max_fd)
max_fd = connfd;

if( i > maxi)


maxi = i;

if(--nready <= 0)
continue;
}

for( i =0; i <=maxi; i++)


{
bzero(line, sizeof(line));

if((sock_fd = client[i]) <0)


continue;

if( FD_ISSET(sock_fd, &rset))


{
n= read(sock_fd, line, sizeof(line));
if( n == 0)
{
close(sock_fd);
FD_CLR(sock_fd, &allset);
client[i] = -1;

bzero(buffer, sizeof(buffer));
sprintf(buffer, "Client %d has left chat\n", conn[sock_fd]);

k = client_no;
while(k > 0 )
{
if(chat[k] > 0)
n = write(chat[k--], buffer, strlen(buffer));
}
}
else
{
if(chat[line[0] - 48] > 0)
write(chat[line[0] - 48], line, strlen(line));
}

if(--nready <= 0)
break;
}
}
}

Output

The server is started first. Each client then starts. The clients are numbered consecutively. If client x
wants to send message to client y, client x writes
y From x: “contents of message”

The screenshots show 3 clients chatting with each other through the server running on port 5500.

Server
Client 1

Client 2
Client 3

You might also like