Department of Cse/It SCSX1024 Network Programming and Management Unit-Ii Iii Year / Vi Sem Application Development 10 Hrs
Department of Cse/It SCSX1024 Network Programming and Management Unit-Ii Iii Year / Vi Sem Application Development 10 Hrs
1. The client reads the input data from the standard input and writes the line to the server.
2. The server reads the data from the network input and echoes the line back to client.
3. The client reads the echoed line and prints it on the standard output.
fgets
write read
Stdin TCP client TCP server
The role of the TCP echo server is written in two functions. One is the main() function
and the other is str_echo() function.
TCP echo server main() function includes the following steps namely,
∑ Create socket
∑ Bind server’s well known port
∑ Wait for client connection to complete
∑ Concurrent server
TCP echo server str_echo() function includes the following steps namely,
∑ Read a buffer
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
Fig. 2.2 shows the TCP echo server main() function and Fig. 2.3 shows the TCP echo
server str_echo() function.
Line 1: It is the header created by the WRS which encapsulates a large number of header that
are required for the functions that are referred.
Line 2 – 3: This the definition of the main() with command line arguments.
Line 5-8 : These are variable declarations of types that are used.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
Line 9 : It is the system call to the socket function that returns a descriptor of type int.- in this
case it is named as listenfd. The arguments are family type, stream type and protocol argument
– normally 0)
Line 10: the function bzero() sets the address space to zero.
Line 11-12: Sets the internet socket address to wild card address and the server port to the
number defined in SERV_PORT which is 9877 (specified by WRS). It is an intimation that the
server is ready to accept a connection destined for any local interface in case the system is
multi homed.
Line 14 :bind () function binds the address specified by the address structure to the socket.
Line 15: The socket is converted into listening socket by the call to the listen()function
Line 17-18: The server blocks in the call to accept, waiting for a client connection to complete.
Line 19 – 24: For each client, fork() spawns a child and the child handles the new client. The
child closes the listening socket and the parent closes the connected socket The child then calls
str_echo () to handle the client.
∑ Connect to server
The TCP echo client str_cli() function includes the following steps namely,
∑ Read a line, write to server
∑ Read echoed line from server, write to standard output
∑ Return to main
Fig. 2.4 shows the TCP echo client main() function and Fig. 2.5 shows the TCP echo client
str_cli() function.
Line 13: The function inet_pton () converts the argument received at the command line from
presentation to numeric value and stores the same at the address specified as the third
arguments.
Line 14 –1 5: Connection function establishes the connection with the server. The function
str_cli () than handles the client processing.
3. Kernel can generate and send signal to process when something happens. (eg)
SIGPIPE will be generated when a process writes to a pipe which has been closed by
the reader.
2.2 POSIX signal handling – Portable Operating System Interface for UNIX
• Every signal has a disposition, which is called the action associated with the signal. The
disposition is set by calling the sigactionfunction.
2.2.1 Choices for disposition
There are three choices for the disposition. These include the following.
a) Whenever a specific signal occurs, a specific function can be provided. This function is called
signal handler and the action is called catching the signal.
• The two signal SIGKILL and SIGSTOP cannot be caught – this is an exception.
• The function is called with a single integer argument that is the signal number and the function
returns nothing as shown below:
conststructsigaction act;
sigaction (SIGCHLD, &act, NULL)
• Calling sigactionand specifying a function to be called when the signal occurs is all that is
required to catch the signal.
ÿ For few signal like SIGIO, SIGPOLL, and SIGURG etc additional actions on the part of
the process is required to catch the signal.
b) A signal a can be ignored by setting its disposition to SIG_IGN. Again the two signals
SIGKILL and SIGSTOP are exceptions.
c) We can set the default disposition for a signal by setting its disposition to SIG_DFL. The
default is normally to terminate a process on the receipt of a signal, with certain signal also
generating a core image of the process in its current working directory. The signals whose
default disposition is to be ignored are : SIGCHLD AND SIGURG(sent on arrival of out of band
data.)
With appropriate settings in the sigaction structure you can control the current process's
response to receiving a SIGCHLD signal. As well as setting a signal handler, other behavior can
be set.
• act.sa_handler is SIG_DFL then the default behaviour will be restored
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
• act.sa_handler is SIG_IGN then the signal will be ignored if possible (SIGSTOP and SIGKILL
can't be ignored)
• act.sa_flags is SA_NOCLDSTOP - SIGCHLD won't be generated when children stop.
• act.sa_flags is SA_NOCLDWAIT - child processes of the calling process will not be
transformed into zombie processes when they terminate.
Fig. 2.6 shows the signal function that calls the POSIX sigaction function. Follwing is the
description of each line.
line 2-3 call to the function when a signal occurs. It has pointer to signal handling function as the
second argument
Line 6: Set Handler : The sa_handler member of the sigaction structure is set to the func
argument
Line 7: Set signal mask to handler: POSIX allows us to specify a set of signals that will be
blocked when our signal handler is called. Any signal that is blocked cannot be delivered to the
process. We set the sa_mask member to the empty set, which means that no additional signals
are blocked while our signal handler is running Posix guarantees that he signal being caught is
always blocked while its handler is executing
Line 8 – 17: An optional flag SA_RESTART, if it is set, a system call interrupted by this signal
will automatically restarted by the kernal.
Line 18 – 20: The function sigaction is called and then return the old action for the signal as the
return value of the signal function.
Fig. 2.6 signal function that calls the POSIX sigaction function
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
Server
A serve socket
on a port
A socket for a A socket for a
client client
Zombies
∑ Zombie = a process that has terminated, but whose parent has not yet waited for it
∑ One way to see zombies is to press Ctrl-Z (suspend) in the midst of execution and then
enter a ps command. Zombies appear as <defunct> processes.
Wait / Waitpid
wait (and waitpid in it's blocking form) temporarily suspends the execution of a parent process
while a child process is running. Once the child has finished, the waiting parent is restarted.
Declarations:
#include <sys/types.h>
#include <sys/wait.h>
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
wait() waitpid()
4. Boundary conditions
4.1 Connection abort before accept returns
After the three way handshake, the connection is established and then the client TCP sends
an RST(reset). ON the server side the connection is queued by its TCP waiting for the server
process to call acceptwhen the RST arrives. Sometime later the server process calls
accept.Depending on the type of implementation the aborted connection differs.
v The Berkley derivedimplementation handles the aborted connection completely within
the kernel and the server process neversees it.
v Most of the SVR4 (System V release 4) implementation returns an error to the
process as the returnfrom accept and the type of error depends on the
implementation.
v Most implementation returns anerronoEPROTO (protocol error) but posix.1g
specifies that the return must be ECONNABORTED. The reason for this is that
EPROTO is also returned when some fatalprotocol related event occurs on the
bytestream. Receiving the same error EPROTO by the server makes itdifficult to
decide whether to call accept again or not. IN case of ECONNABORTED error,
theserverignores the error and just calls accept again.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
When the client has more than one to write, what happens to it? That is when the FIN is
received,the readline returns RST, but the second one is also written. The rule applied here is,
when a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the
process. The default action ofthis signal is to terminate the process so the process must catch
the signal to avoid involuntarilyterminated.If the process catches the signal and returns from the
signal handler, or ignores the signal, the writeoperation returns EPIPE (error pipe)
#include <unp.h>
voidstr_cli (FILE *fp, intsockfd)
{
charsendline[MAXLINE], recvline[MAXLINE];
while (fgets(sendline, MAXLINE, fp)!=null)
{
writen(sockfd, sendline, 1);
sleep(1);
writeln (sockfd, sendline +1, strlen (sendline)-1);
if (readline()sockfd, recvline, MAXLINE==0)
err_quit (―str_cli: server terminated prematurelyǁ);
fputs (recvline, stdout);
}
}
In the above str_cli(), the writeln is called two times: the first time the first byte of data is
writeln tothe socket, followed by a pause of 1 sec, followed by the remainder of the line. The
intention is for the firstwriteln to elicit the RST and then for the second writeln to generate
SIGPIPE.We start with the client, type in one line, see that line is echoed correctly, and then
terminates theserver child on the server host, we then type another line, but nothing is echoed
and we just get a shellprompt. Since the default action of the SIGPIPE is to terminate the
process without generating a core file,nothing is printed by the Kornshell.The recommended
way to handle SIGPIPE depends on what the application what to do when thisoccurs. IF there is
nothing special to do, then setting the signal disposition to SIG_IGN is easy, assumingthat
subsequent output operations will catch the error of EPIPE and terminate.IF special actions are
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
needed when the signal occurs, then the signal should be caught and anydesired actions can
be performed in the signal handler.If multiple sockets are in use, the delivery of the signal does
not tell us which socket encountered theerror. IF we need to know which write caused the error,
then we must either ignore the signal or return fromthe signal handler and handle EPIPE from
write.
4.3 Crashing of Server Host
This scenario lets us know what happens when the server host crashes. To simulate this we
must run theclient and server on different hosts. We then start server, start the client, type in a
line to the client toverify that the connection is up, disconnect the server host from the network,
andtype in another line at the client. This also covers the scenario of the server host being
unreachablewhen the client sends data (some immediate router is down after the connection
has been established).
The following steps take place.
1. When the server host crashes, nothing is sent out on the existing network connections. That
iswe are assuming the host crashes, and is not shut down by the operator.
2. We type a line of input to the client, it is written by writen and is sent by the client TCP as a
data segment. The client then blocks in the call to readline waiting for the echoed reply.
3. If we watch the network with tcpdump, we will see the client TCP continually retransmit
thedata segment, trying to receive ACK from the server. Berkley derived implementations
transmitthe date segments 12 times, waiting around 9 minutes before giving up. When the client
finallygives up, an error is returned to the client process. Since the client is blocked in the call to
readline, it returns an error. Assuming the server host had crashed and there were no
responsesat all to the client‘s data segments, the error is ETIMEDOUT. But if some intermediate
routerdetermine that the server was unreachable and responded with ICMP destination
unreachablemessage, then error is either EHOSTUNREACH or ENETUNREACH.
∑ To detect that the server is unreachable even before 9 minutes, place a time out call to
readline.
∑ To find the crash of server even if client is not sending data actively, another technique
is usedwhich used SO_KEEPALIVE socket option
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
In the following example, we will establish a connection between the client and server and then
assume the server host crashes and reboots. The easiest way to simulate this is to establish the
connection, disconnect the server from the network, shut down the server host and then reboot
it, and then reconnect the server host to the network. We do not want the client to see the
server host shut down.
As stated in the previous section, if the client is not actively sending data to the server when the
server host crashes, the client is not aware that the server host has crashed. The following
steps take place:
1. We start the server and then the client. We type a line to verify that the connection is
established.
2. The server host crashes and reboots.
3. We type a line of input to the client, which is sent as a TCP data segment to the server
host.
4. When the server host reboots after crashing, its TCP loses all information about
connections that existed before the crash. Therefore, the server TCP responds to the
received data segment from the client with an RST.
5. Our client is blocked in the call to readline when the RST is received, causing readline to
return the error ECONNRESET.
If it is important for our client to detect the crashing of the server host, even if the client is not
actively sending data, then some other technique, such as the SO_KEEPALIVE socket option or
some client/server heartbeat function, is required.
of steps discussed under termination of server processWe need to select the select or poll
function in the client to have the client detect thetermination of the server process as soon it
occurs.
5. I/O Multiplexing
• TCP echo client is handling two inputs at the same time: standard input and a TCP
socket
ÿ when the client was blocked in a call to read, the server process was killed
ÿ server TCP sends FIN to the client TCP, but the client never sees FIN since the
client is blocked reading from standard input
¸ We need the capability to tell the kernel that we want to be notified if one
or more I/O conditions are ready.
¸ I/O multiplexing (select, poll, or newer pselect functions)
• Scenarios for I/O Multiplexing
ÿ client is handling multiple descriptors (interactive input and a network socket).
ÿ Client to handle multiple sockets (rare)
ÿ TCP server handles both a listening socket and its connected socket.
ÿ Server handle both TCP and UDP.
ÿ Server handles multiple services and multiple protocols
6. I/O Models
∑ blocking I/O
∑ nonblocking I/O
∑ Waiting for the data to be ready (for a socket, wait for the data to arrive
on the network, then copy into a buffer within the kernel)
∑ Copying the data from the kernel to the process (from kernel buffer into
application buffer)
Categories
• Synchronous I/O
ÿ causes the requesting process to be blocked until that I/O operation (recvfrom)
completes. (blocking, nonblocking, I/O multiplexing, signal-driven I/O)
• Asynchronous I/O
UDP is used in this example instead of TCP because with UDP, the concept of data
being "ready" to read is simple: either an entire datagram has been received or it has not. With
TCP it gets more complicated, as additional variables such as the socket's low-water mark
come into play.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
In fig. 2.8, the process calls recvfrom and the system call does not return until the
datagram arrives and is copied into our application buffer, or an error occurs. The most common
error is the system call being interrupted by a signal, The process is blocked the entire time from
when it calls recvfrom until it returns. When recvfrom returns successfully, the application
processes the datagram.
∑ For the first three recvfrom, there is no data to return and the kernel immediately returns
an error of EWOULDBLOCK.
∑ For the fourth time we call recvfrom, a datagram is ready, it is copied into our application
buffer, and recvfrom returns successfully. The data is further processed.
When an application sits in a loop calling recvfrom on a nonblocking descriptor like this, it is
called polling. The application is continually polling the kernel to see if some operation is ready.
This is often a waste of CPU time, but this model is occasionally encountered, normally on
systems dedicated to one function.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
We block in a call to select, waiting for the datagram socket to be readable. When select returns
that the socket is readable, we then call recvfrom to copy the datagram into our application
buffer.
∑ Disadvantage: using select requires two system calls (select and recvfrom) instead of
one
∑ Advantage: we can wait for more than one descriptor to be ready.
Another closely related I/O model is to use multithreading with blocking I/O. That model
very closely resembles the model described above, except that instead of using select to block
on multiple file descriptors, the program uses multiple threads (one per file descriptor), and each
thread is then free to call blocking system calls like recvfrom.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
The signal-driven I/O model uses signals, telling the kernel to notify us with
the SIGIO signal when the descriptor is ready. Fig. 2.11 shows the signal driven I/O model.
∑ We first enable the socket for signal-driven I/O and install a signal handler using
the sigaction system call. The return from this system call is immediate and our process
continues; it is not blocked.
∑ When the datagram is ready to be read, the SIGIO signal is generated for our process.
We can either:
o read the datagram from the signal handler by calling recvfrom and then notify the
main loop that the data is ready to be processed.
o notify the main loop and let it read the datagram.
The advantage to this model is that we are not blocked while waiting for the datagram to
arrive. The main loop can continue executing and just wait to be notified by the signal handler
that either the data is ready to process or the datagram is ready to be read.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
These functions work by telling the kernel to start the operation and to notify us when the
entire operation (including the copy of the data from the kernel to our buffer) is complete. The
main difference between this model and the signal-driven I/O model is that with signal-driven
I/O, the kernel tells us when an I/O operation can be initiated, but with asynchronous I/O, the
kernel tells us when an I/O operation is complete. Fig. 2.12 shows the asynchronous I/O model.
∑ We call aio_read (the POSIX asynchronous I/O functions begin with aio_ or lio_) and
pass the kernel the following:
o descriptor, buffer pointer, buffer size (the same three arguments for read),
o file offset (similar to lseek),
o and how to notify us when the entire operation is complete.
This system call returns immediately and our process is not blocked while waiting for the
I/O to complete.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
The main difference between the first four models is the first phase, as the second
phase in the first four models is the same: the process is blocked in a call to recvfrom while the
data is copied from the kernel to the caller's buffer. Asynchronous I/O, however, handles both
phases and is different from the first four.
6. SELECT FUNCTION
• Allows the process to instruct the kernel to wait for any one of multiple events to occur
and to wake up the process only when one or more of these events occurs or when a
specified amount of time has passed.
• Wait forever : return only when descriptor (s) is ready (specify timeout argument as
NULL)
• Do not wait at all : return immediately after checking the descriptors. Polling (specify
timeout argument as pointing to a timeval structure where the timer value is 0)
• The wait is normally interrupted if the process catches a signal and returns from the
signal handler
6.2 Syntax
#include <sys/select.h>
#include <sys/time.h>
int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const
struct timeval *);
//Returns: +ve count of ready descriptors, 0 on timeout, -1 on error
struct timeval{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */ }
¸ the presence of control status information to be read from the master side
of a pseudo terminal (Ignore)
• If you pass the 3 arguments as NULL, you have a high precision timer than the sleep
function
Descriptor Sets
• 4 macros
ÿ void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */
ÿ void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset*/
fd_set rset;
• The maxfdp1 argument specifies the number of descriptors to be tested. Its value is the
maximum descriptor to be tested plus one. The descriptors 0, 1, 2, up through and
including maxfdp1–1 are tested.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
• The reason the maxfdp1 argument exists, along with the burden of calculating its value,
is for efficiency. Although each fd_set has room for many descriptors, typically 1,024,
this is much more than the number used by a typical process. The kernel gains
efficiency by not copying unneeded portions of the descriptor set between the process
and the kernel, and by not testing bits that are always 0.
1. A socket is ready for reading if any of the following four conditions is true:
o The number of bytes of data in the socket receive buffer is greater than or equal
to the current size of the low-water mark for the socket receive buffer. A read
operation on the socket will not block and will return a value greater than 0 (i.e.,
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
the data that is ready to be read). We can set this low-water mark using
the SO_RCVLOWAT socket option. It defaults to 1 for TCP and UDP sockets.
o The read half of the connection is closed (i.e., a TCP connection that has
received a FIN). A read operation on the socket will not block and will return 0
(i.e., EOF).
o The socket is a listening socket and the number of completed connections is
nonzero.
o A socket error is pending. A read operation on the socket will not block and will
return an error (–1) with errno set to the specific error condition. These pending
errors can also be fetched and cleared by calling getsockopt and specifying
the SO_ERROR socket option.
2. A socket is ready for writing if any of the following four conditions is true:
o The number of bytes of available space in the socket send buffer is greater than
or equal to the current size of the low-water mark for the socket send buffer and
either: (i) the socket is connected, or (ii) the socket does not require a connection
(e.g., UDP). This means that if we set the socket to nonblocking (Chapter 16), a
write operation will not block and will return a positive value (e.g., the number of
bytes accepted by the transport layer). We can set this low-water mark using
the SO_SNDLOWAT socket option. This low-water mark normally defaults to
2048 for TCP and UDP sockets.
o The write half of the connection is closed. A write operation on the socket will
generate SIGPIPE (Section 5.12).
o A socket using a non-blocking connect has completed the connection, or the
connect has failed.
o A socket error is pending. A write operation on the socket will not block and will
return an error (–1) with errno set to the specific error condition. These pending
errors can also be fetched and cleared by calling getsockopt with
theSO_ERROR socket option.
3. A socket has an exception condition pending if there is out-of-band data for the socket or
the socket is still at the out-of-band mark.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
pselect Function
The pselect function was invented by POSIX and is now supported by many of the Unix
variants.
#include <sys/select.h>
#include <signal.h>
#include <time.h>
int pselect (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
pselect uses the timespec structure (another POSIX invention) instead of the timeval structure.
The tv_nsec member of the newer structure specifies nanoseconds, whereas
the tv_usec member of the older structure specifies microseconds.
struct timespec {
};
pselect adds a sixth argument: a pointer to a signal mask. This allows the program to disable
the delivery of certain signals, test some global variables that are set by the handlers for these
now-disabled signals, and then call pselect, telling it to reset the signal mask.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
With regard to the second point, consider the following example (discussed on APUE). Our
program's signal handler for SIGINT just sets the global intr_flag and returns. If our process is
blocked in a call to select, the return from the signal handler causes the function to return
with errno set to EINTR. But when select is called, the code looks like the following:
if (intr_flag)
if (errno == EINTR) {
if (intr_flag)
handle_intr();
...
The problem is that between the test of intr_flag and the call to select, if the signal occurs, it will
be lost if select blocks forever.
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
if (intr_flag)
if (errno == EINTR) {
if (intr_flag)
handle_intr ();
...
Before testing the intr_flag variable, we block SIGINT. When pselect is called, it replaces the
signal mask of the process with an empty set (i.e., zeromask) and then checks the descriptors,
possibly going to sleep. But when pselect returns, the signal mask of the process is reset to its
value before pselect was called (i.e., SIGINT is blocked).
7. SHUTDOWN function
ÿ send FIN to server, but leave the socket descriptor open for reading
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
ÿ decrements the descriptor’s reference count and closes the socket only if the
count reaches 0
¸ With shutdown, we can tell other end that we are done sending, although
that end might have more data to send us.
7.1 Syntax
#include<sys/socket.h>
• howto argument
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
ÿ SHUT_RD
¸ Any data received after this call is ACKed and then discarded
ÿ SHUT_WR
ÿ SHUT_RDWR
¸ both closed
The TCP echo server as a single process that uses select to handle any number of
clients, instead of forking one child per client.
Before the first client has established a connection, the server has a single listening descriptor
as shown in fig. 2.15.
∑ The server maintains only a read descriptor set (rset), shown in the following figure.
Assuming the server is started in the foreground, descriptors 0, 1, and 2 are set to
standard input, output, and error, so the first available descriptor for the listening socket
is 3.
∑ We also show an array of integers named client that contains the connected socket
descriptor for each client. All elements in this array are initialized to –1.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
The only nonzero entry in the descriptor set is the entry for the listening sockets and the first
argument to select will be 4.
When the first client establishes a connection with our server, the listening descriptor
becomes readable and our server calls accept. The new connected descriptor returned by
accept will be 4. Fig. 2.16 shows this connection:
The server must remember the new connected socket in its client array, and the
connected socket must be added to the descriptor set. The updated data structures are shown
in the fig. 2.17.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
Sometime later a second client establishes a connection and we have the scenario
shown below as in fig. 2.18.
The new connected socket (which we assume is 5) must be remembered, giving the data
structures shown below as in fig. 2.19.
The first client terminates its connection. The client TCP sends a FIN, which makes descriptor 4
in the server readable. When our server reads this connected socket, read returns 0. We then
close this socket and update our data structures accordingly. The value of client[0] is set to –1
and descriptor 4 in the descriptor set is set to 0. This is shown in the figure below. Notice that
the value of maxfd does not change.
∑ As clients arrive, we record their connected socket descriptor in the first available entry
in the client array (the first entry with a value of –1) and also add the connected socket to
the read descriptor set.
∑ The variable maxi is the highest index in the client array that is currently in use and the
variable maxfd (plus one) is the current value of the first argument to select.
∑ The only limit on the number of clients that this server can handle is the minimum of the
two values FD_SETSIZE and the maximum number of descriptors allowed for this
process by the kernel
/* include fig01 */
#include "unp.h"
int
main(int argc, char **argv)
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Listen(listenfd, LISTENQ);
/* include fig02 */
for ( ; ; ) {
rset = allset; /* structure assignment */
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
if (--nready <= 0)
continue; /* no more readable descriptors */
}
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/* connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
∑ Create listening socket and initialize for select. We create the listening socket using
socket, bind, and listen and initialize our data structures assuming that the only descriptor
that we will select on initially is the listening socket.
∑ Block in select. select waits for something to happen, which is one of the following:
o The establishment of a new client connection.
o The arrival of data on the existing connection.
o A FIN on the existing connection.
o A RST on the existing connection.
∑ accept new connections.
o If the listening socket is readable, a new connection has been established.
o We call accept and update our data structures accordingly. We use the first
unused entry in the client array to record the connected socket.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
This server is more complicated than the earlier version, but it avoids all the overhead of
creating a new process for each client and it is a nice example of select. Nevertheless, in, we
will describe a problem with this server that is easily fixed by making the listening socket
nonblocking and then checking for, and ignoring, a few errors from accept.
Denial-of-Service Attacks
There is a problem with the server in the above example. If a malicious client connects
to the server, sends one byte of data (other than a newline), and then goes to sleep. The server
will call read, which will read the single byte of data from the client and then block in the next
call to read, waiting for more data from this client. The server is then blocked ("hung") by this
one client and will not service any other clients, until the malicious client either sends a newline
or terminates.
The basic concept here is that when a server is handling multiple clients, the server can
never block in a function call related to a single client. Doing so can hang the server and deny
service to all other clients. This is called a denial-of-service attack, which prevents the server
from servicing other legitimate clients.
9. Poll Function
Poll provides functionality that is similar to select, but poll provides additional information
when dealing with STREAMS devices.
#include <poll.h>
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);
Arguments:
The first argument (fdarray) is a pointer to the first element of an array of structures. Each
element is a pollfd structure that specifies the conditions to be tested for a given descriptor, fd.
struct pollfd {
int fd; /* descriptor to check */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
The conditions to be tested are specified by the events member, and the function returns the
status for that descriptor in the corresponding revents member. This data structure (having two
variables per descriptor, one a value and one a result) avoids value-result arguments (the
middle three arguments for select are value-result). Each of these two members is composed of
one or more bits that specify a certain condition. The following figure shows the constants used
to specify the events flag and to test the revents flag against.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
The first four constants deal with input, the next three deal with output, and the final three deal
with errors. The final three cannot be set in events, but are always returned in revents when the
corresponding condition exists.
With regard to TCP and UDP sockets, the following conditions cause poll to return the specified
revent. Unfortunately, POSIX leaves many holes (optional ways to return the same condition) in
its definition of poll.
∑ All regular TCP data and all UDP data is considered normal.
∑ TCP's out-of-band data is considered priority band.
∑ When the read half of a TCP connection is closed (e.g., a FIN is received), this is also
considered normal data and a subsequent read operation will return 0.
∑ The presence of an error for a TCP connection can be considered either normal data or
an error (POLLERR). In either case, a subsequent read will return –1 with errno set to
the appropriate value. This handles conditions such as the receipt of an RST or a
timeout.
∑ The availability of a new connection on a listening socket can be considered either
normal data or priority data. Most implementations consider this normal data.
∑ The completion of a nonblocking connect is considered to make a socket writable.
The number of elements in the array of structures is specified by the nfds argument.
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
The timeout argument specifies how long the function is to wait before returning. A
positive value specifies the number of milliseconds to wait. The constant INFTIM (wait forever)
is defined to be a negative value.
∑ –1 if an error occurred
∑ 0 if no descriptors are ready before the timer expires
∑ Otherwise, it is the number of descriptors that have a nonzero revents member.
If we are no longer interested in a particular descriptor, we just set the fd member of the pollfd
structure to a negative value. Then the events member is ignored and the revents member is
set to 0 on return.
In the select version we allocate a client array along with a descriptor set named rset).
With poll, we must allocate an array of pollfd structures to maintain the client information instead
of allocating another array. We handle the fd member of this array the same way we handled
the client array in the selection version: a value of –1 means the entry is not in use; otherwise, it
is the descriptor value. Any entry in the array of pollfd structures passed to poll with a negative
value for the fd member is just ignored.
/* include fig01 */
#include "unp.h"
#include <limits.h> /* for OPEN_MAX */
int
main(int argc, char **argv)
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
char buf[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Listen(listenfd, LISTENQ);
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* -1 indicates available entry */
maxi = 0; /* max index into client[] array */
/* end fig01 */
/* include fig02 */
for ( ; ; ) {
nready = Poll(client, maxi+1, INFTIM);
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
} else
err_sys("read error");
} else if (n == 0) {
/* connection closed by client */
#ifdef NOTDEF
printf("client[%d] closed connection\n", i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
∑ Call poll, check for new connection. We call poll to wait for either a new connection or
data on existing connection.
o When a new connection is accepted, we find the first available entry in the client
array by looking for the first one with a negative descriptor.
o We start the search with the index of 1, since client[0] is used for the listening
socket.
o When an available entry is found, we save the descriptor and set the
POLLRDNORM event.
∑ Check for data on an existing connection. The two return events that we check for
are POLLRDNORM and POLLERR. We did not set POLLERR in the events member
because it is always returned when the condition is true. The reason we check for
POLLERR is because some implementations return this event when an RST is received
for a connection, while others just return POLLRDNORM. In either case, we call read
and if an error has occurred, it will return an error. When an existing connection is
terminated by the client, we just set the fd member to –1.
The following code is our revised and correct version of the str_cli function that
uses select and shutdown. In the function,select notifies us as soon as the server closes its end
of the connection and shutdown lets us handle batch input correctly.
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
DEPARTMENT OF CSE/IT
SCSX1024 NETWORK PROGRAMMING AND MANAGEMENT UNIT- II
III YEAR / VI SEM
stdineof = 0;
FD_ZERO(&rset);
for ( ; ; ) {
if (stdineof == 0)
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
∑ stdineof is a new flag that is initialized to 0. As long as this flag is 0, each time around
the main loop, we select on standard input for readability.
∑ Normal and premature termination. When we read the EOF on the socket, and:
o If we have already encountered an EOF on standard input, this is normal
termination and the function returns.
o If we have not yet encountered an EOF on standard input, the server process
has prematurely terminated. We now callread and write to operate on buffers
instead of lines and allow select to work for us as expected.
∑ shutdown. When we encounter the EOF on standard input, our new flag, stdineof, is set
and we call shutdown with a second argument of SHUT_WR to send the FIN. Here
buffers are used instead of lines, using read and writen.