Upload
monicadoss85
View
245
Download
0
Embed Size (px)
Citation preview
8/22/2019 NPM Unit IIIfinal
1/30
MC9241-Network Programming
1
Unit III
Application Development
TCP Echo server:
A TCP echo server performs the following steps:
1. The client reads a line of text from its standard input and writes the line to the server.2. The server reads the line from its network input and echoes the line back to the client.3. The client reads the echoed line and prints it on its standard output.The following figure depicts the simple client/server along with the functions used for
input and output.
FIG: SIMPLE ECHO CLIENT AND SERVER
main() function:
Fig: TCP echo server - CONCURRENT SERVER
1 #include "unp.h"
2 int3 main(int argc, char **argv)
4 {
5 int listenfd, connfd;
6 pid_t childpid;
7 socklen_t clilen;
8 struct sockaddr_in cliaddr, servaddr;
9 listenfd = Socket (AF_INET, SOCK_STREAM, 0); // Socket created
10 bzero(&servaddr, sizeof(servaddr)); // servaddr socket address structure initialized to 0
11 servaddr.sin_family = AF_INET;
12 servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
13 servaddr.sin_port = htons (SERV_PORT);
14 Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); // servaddr is assigned to socket
15 Listen(listenfd, LISTENQ); // Socket is converted to listening soc.
16 for ( ; ; ) {
17 clilen = sizeof(cliaddr);
8/22/2019 NPM Unit IIIfinal
2/30
MC9241-Network Programming
2
18 connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
19 if ( (childpid = Fork()) == 0) { /* child process */
20 Close(listenfd); /* close listening socket */
21 str_echo(connfd); /* process the request */
22 exit (0);
23 }
24 Close(connfd); /* parent closes connected socket */
25 }
26 }
5-8 variables declaration
9 Create socket: Socket() function is used to create a socket.
11-13 bind server's well-known port: An Internet socket address structure is filled in with
the wildcard address - INADDR_ANY and the server's well-known port - SERV_PORT.
1415 Bind and Listen: Binding the wildcard address tells the system that we will accept a
connection destined for any local interface. Our choice of the TCP port number should be
greater than 1023, greater than 5000, less than 49152. The socket is converted into a listening
socket by listen() function.
17
18 Wait for client connection to complete: The server blocks in the call to accept,
waiting for a client connection to complete.
1924 Concurrent server: For each client, fork spawns a child, and the child handles the
new client.
21 str_echo: Function which contains read and write operation is shown below.
str_echo function:
This function performs the server processes.
Fig: str_echo function: echoes data on a socket.
1 #include "unp.h"
2 void
3 str_echo(int sockfd)
4 {
5 ssize_t n;
6 char line[MAXLINE];
8/22/2019 NPM Unit IIIfinal
3/30
MC9241-Network Programming
3
7 for(;;) {
8 if ( (n = readline(sockfd, line, MAXLINE)) = = 0)
9 return;
10 Writen(sockfd, line, n);
11 }}
The function str_echo, performs the server processing for each client: It reads data from the
client and echoes it back to the client.
89 Read a buffer and echo the buffer: readline reads data from the socket and the line is
echoed back to the client by writen. If the client closes the connection (the normal scenario),
the receipt of the client's FIN causes the child's read to return 0. This causes the str_echo
function to return.
TCP echo client:
main() function:
Fig: TCP echo client.
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd;
6 struct sockaddr_in servaddr;
7 if (argc != 2)
8 err_quit("usage: tcpcli ");
9 sockfd = Socket(AF_INET, SOCK_STREAM, 0); // socket is created
10 bzero(&servaddr, sizeof(servaddr));
11 servaddr.sin_family = AF_INET;
12 servaddr.sin_port = htons(SERV_PORT);
13 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
14 Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); // connection establised
15 str_cli(stdin, sockfd); /* do it all */
16 exit(0);
17 }
8/22/2019 NPM Unit IIIfinal
4/30
MC9241-Network Programming
4
5-8 variables declaration
9 Create socket: Socket() function is used to create a socket.
1013 Fill in Internet socket address structure: A TCP socket is created and an Internet
socket address structure is filled in with the server's IP address and port number. We take the
server's IP address from the command-line argument and the server's well-known port
(SERV_PORT) is from our unp.h header.
1415 Connect to server; connect establishes the connection with the server. The function
str_cli handles the rest of the client processing. Str_cli function defined below.
str_cli function:This function, handles the client processing loop:
1. It reads a line of text from standard input2. Writes it to the server3. Reads back the server's echo of the line, and4. Outputs the echoed line to standard output.
Figure - str_cli function: client processing loop.
1 #include "unp.h"
2 void
3 str_cli(FILE *fp, int sockfd)
4 {
5 char sendline[MAXLINE], recvline[MAXLINE];
6 while (Fgets(sendline, MAXLINE, fp) != NULL) // read input from stdin device
{
7 Writen(sockfd, sendline, strlen (sendline)); // write into socket
8 if (Readline(sockfd, recvline, MAXLINE) = = 0) // read from socket
9 err_quit("str_cli: server terminated prematurely");
10 Fputs(recvline, stdout); // output the echoed line
11 }
12 }
8/22/2019 NPM Unit IIIfinal
5/30
MC9241-Network Programming
5
67 Read a line, write to server: fgets reads a line of text and writen sends the line to the
server.
810 Read echoed line from server, write to standard output: readline reads the line
echoed back from the server and fputs writes it to standard output.
1112 Return to main: The loop terminates when fgets returns a null pointer, which occurs
when it encounters either an end-of-file (EOF) or an error. Our Fgets wrapper function
checks for an error and aborts if one occurs, so Fgets returns a null pointer only when an end-
of-file is encountered.
OUTPUT:
% TcpEchoServer
% TcpEchoClient 127.0.0.1
Hi u there
Hi u there
^D (EOF)
We need to check how the Echo server and client works with normal startup and
normal termination to build robust client server.
POSIX signal handling:
POSIX is set of standards for unix standardization, developed by IEEE and adopted
by IEC/ISO. POSIX defines Network API standard which defines DNI/SOC and DNI/XTI
Signal:
A signal is a notification to a process that an event has occurred. Signals are
sometimes called software interrupts. Signals usually occur asynchronously i.e a process
doesn't know ahead of time exactly when a signal will occur.
Signals can be sent:
By one process to another process (or to itself) By the kernel to a process
8/22/2019 NPM Unit IIIfinal
6/30
MC9241-Network Programming
6
Signal disposition:
Every signal has a disposition, which is also called the action associated with the signal. We
have three choices for the disposition:
1. Providing a function: We can provide a function that is called whenever a specificsignal occurs. This function is called a signal handler and this action is called
catching a signal. Function prototype is void handler (int signo);
2. Ignore a signal: We can ignore a signal by setting its disposition to SIG_IGN.3. Default action for a signal: 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 receipt
of a signal, with certain signals also generating a core image of the process in its
current working directory.
There are a few signals whose default disposition is to be ignored:
Signal Function
The POSIX way to establish the disposition of a signal is to call the sigaction
function.
SignoSignal Number / Signal Name
*actPoints to Signal Action
*oactPoints to Signal Old Action
One argument to the function is a structure that we must allocate and fill in. An easier
way to set the disposition of a signal is to call the signal function.
Sigfunc *signal(int signo, sigfunc *func);
SignoSignal Number / Signal Name
*func - Pointer to a function or one of the constants SIG_IGN or SIG_DFL.
Signal - Function that predates POSIX.
Signal function that calls the POSIX sigaction function.
1 #include "unp.h"
2 Sigfunc *
3 signal (int signo, Sigfunc *func)
4 {
5 struct sigaction act, oact;
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
8/22/2019 NPM Unit IIIfinal
7/30
MC9241-Network Programming
7
6 act.sa_handler = func;
7 sigemptyset (&act.sa_mask);
8 act.sa_flags = 0;
9 if (signo == SIGALRM) {
10 #ifdef SA_INTERRUPT
11 act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
12 #endif
13 } else {
14 #ifdef SA_RESTART
15 act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */
16 #endif
17 }
18 if (sigaction (signo, &act, &oact) < 0)
19 return (SIG_ERR);
20 return (oact.sa_handler);
21 }
23 Simplify function prototype using typedef: The normal function prototype for signal is complicated by the level of nested
parentheses.
void (*signal (int signo, void (*func) (int))) (int);
To simplify this, we define the Sigfunc type in our unp.h header astypedef void Sigfunc(int);
Stating that signal handlers are functions with an integer argument and the functionreturns nothing (void). The function prototype then becomes
sigfunc *signal (int signo, sigfunc *func);
A pointer to a signal handling function is the second argument to the function, as wellas the return value from the function.
6 Set handler:The sa_handler member of the sigaction structure is set to the function argument.
7 Set signal mask for 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 a process.
8/22/2019 NPM Unit IIIfinal
8/30
MC9241-Network Programming
8
We set the sa_mask member to the empty set, which means that no additional signalswill be blocked while our signal handler is running.
POSIX guarantees that the signal being caught is always blocked while its handler isexecuting.
817 Set SA_RESTART flag:SA_RESTART is an optional flag. When the flag is set, a system call interrupted by
this signal will be automatically restarted by the kernel. If the signal being caught is not
SIGALRM, we specify the SA_RESTART flag, if defined. Some older systems, notably
SunOS 4.x, automatically restart an interrupted system call by default and then define the
complement of this flag as SA_INTERRUPT. If this flag is defined, we set it if the signal
being caught is SIGALRM.
1820 Call sigaction: We call sigaction and then return the old action for the signal asthe return value of the signal function.
POSIX Signal Semantics:
Once a signal handler is installed, it remains installed. While a signal handler isexecuting, the signal being delivered is blocked. Furthermore, any additional signals
that were specified in the sa_mask signal set passed to sigaction when the handler was
installed are also blocked.
If a signal is generated one or more times while it is blocked, it is normally deliveredonly one time after the signal is unblocked. Signals are not queued.
It is possible to selectively block and unblock a set of signals using the sigprocmaskfunction. This lets us protect a critical region of code by preventing certain signals
from being caught while that region of code is executing.
Server with multiple clients:
Wait and Waitpid functions:
Wait and waitpid functions are used to handle terminated child
prototypes:
pid_t wait( i nt * statloc);
pid_t waitpid(pid_t pid, int * statloc, int options
Return process ID on success.
Return 0/-1 on error.
8/22/2019 NPM Unit IIIfinal
9/30
MC9241-Network Programming
9
Both wait and waitpid return 2 values1. Process ID of the terminated child2. Termination status of the child an integer returned through the statloc
pointer.
Thera are 3 macros are used to examine the termination status.The status are 1. Child terminated normally
2. killed by a signal
3. job control is stopped.
Additional macros are defined to fetch exit status of the child, value of the signal that
killed the child and value of the job control signal that stopped the child.
Waitpid:
Waitpid gives us more control than wait Pid_t waitpid(pid_t pid, int *statloc, int options)
1. pid specifies the process ID that we want to wait for.2. 2nd argument is used to return termination status of a child3. 3rd argument allows us to specifies additional options, most commonly
used option is WNOHANG.
a. WNOHANGtells the kernel not to block if there are no terminated children; itblocks only if there are children still executing.
Difference between wait and waitpid:
In the client-server communication, multiple client requests can come into the same
server socket or into the same server port. Client connection requests are queued at the port,
so the server must accept the connections sequentially. However, the server can service them
simultaneously through the use of threadsone per each client connection.
The basic flow of logic in a server:
While(true) { Accept a connection
Create a thread to deal with the client;
End while; }
8/22/2019 NPM Unit IIIfinal
10/30
MC9241-Network Programming
10
Multiple connections from a client:
The client establishes five connections with the server. Each new connection is handled by
the server child which is created by using fork() function. The purpose of establishing
multiple connections is to spawn multiple children from the concurrent server, as shown in
Figure below.
Fig: Client with five established connections to same concurrent server.
Client
Figure - TCP client that establishes five connections with server.
1 #include "unp.h"
2 int
3 main (int argc, char **argv)
4 {
5 int i, sockfd[5];
6 struct sockaddr_in servaddr;
7 if (argc != 2)
8 err_quit ("usage: tcpcli ";
9 for (i = 0; i < 5; i++) {
10 sockfd[i] = Socket (AF_INET, SOCK_STREAM, 0);
11 bzero (&servaddr, sizeof (servaddr));
12 servaddr.sin_family = AF_INET;
13 servaddr.sin_port = htons (SERV_PORT);
14 Inet_pton (AF_INET, argv[1], &servaddr.sin_addr);
15 Connect (sockfd[i], (SA *) &servaddr, sizeof (servaddr));
16 }17 str_cli (stdin, sockfd[0]); /* do it all */
18 exit(0);
19 }
When the client terminates, all open descriptors are closed automatically by thekernel, and all five connections are terminated at about the same time.
This causes five FINs to be sent, one on each connection, which in turn causes all fiveserver children to terminate at about the same time.
4 3 2 1 0 Server
Parent
Server
child #1
Server
child #2
Server
child #3
Server
child #4
Server
child #5
8/22/2019 NPM Unit IIIfinal
11/30
MC9241-Network Programming
11
This causes five SIGCHLD signals to be delivered to the parent at about the sametime, which we show in Figure below.
Figure - Client terminates, closing all five connections, terminating all five children.
What happens, with the multiple occurrences of the same signal and they are not queued.
Assume the echoserver is modified to call signal handler to handle SIGCHLD signal
linux %tcpserv03 &
[1] 20419
linux %tcpcli04 127.0.0.1
hello we type this
hello and it is echoed
^D we then type our EOF characterchild 20426 terminated output by server
We got only one printf statement but we a output stating all 5 children termination. Use PS command to check the status : % ps -l
PID TTY TIME CMD
20419 pts/6 00:00:00 tcpserv03
20421 pts/6 00:00:00 tcpserv03
20422 pts/6 00:00:00 tcpserv03
20423 pts/6 00:00:00 tcpserv03
There are 4 children left as zombies. Establishing a signal handler and calling wait fromhandler are insufficient for preventing zombies
Solution is to call waitpid instead of wait. Final version of sig_chld function , calls waitpid
void sig_chld( int signo)
{
pid_t pid;
int state;
8/22/2019 NPM Unit IIIfinal
12/30
MC9241-Network Programming
12
whi le((pid = waitpid(-1,& state,WNOHANG)>0);
printf(child % terminated,PID);
return;
}
Waitpid is in while loop, fetches the status of any terminated child. We must specify WNOHANG option which informs to waitpid not to block if there
exist running children that have not yet terminated.
Final version of TCPECHOSERVER handles EINTR error
1 #include "unp.h"
2 int
3 main (int argc, char **argv)
4 {
5 int i, sockfd[5];
6 struct sockaddr_in servaddr;
7 if (argc != 2)
8 err_quit ("usage: tcpcli ";
9 for (i = 0; i < 5; i++) {
10 sockfd[i] = Socket (AF_INET, SOCK_STREAM, 0);
11 bzero (&servaddr, sizeof (servaddr));
12 servaddr.sin_family = AF_INET;
13 servaddr.sin_port = htons (SERV_PORT);
14 Inet_pton (AF_INET, argv[1], &servaddr.sin_addr);
15 Connect (sockfd[i], (SA *) &servaddr, sizeof (servaddr));
16 }
17 str_cli (stdin, sockfd[0]); /* do it all */
18 exit(0);
19 }
8/22/2019 NPM Unit IIIfinal
13/30
MC9241-Network Programming
13
Boundary conditions:
When our client and server run in their normal mode, then they have normal startup and
normal termination. Beside the normal case, we examine lots of boundary conditions:
what happens when server process crashes? what happens when server host crashes? what happens when server host is unreachable? what happens when the server crashes and reboots? what happens when server host shutdown?
In those cases, we need complex client and server that can handle these boundary conditions.
Server host crashes:
What happens when the server process crashes?
To simulate this, we follow the preceding steps:
1. We start the server and client and type one line to the client to verify that all is okay.That line is echoed normally by the server child.
2. We find the process ID of the server child and kill it. As part of process termination,all open descriptors in the child are closed. This causes a FIN to be sent to the client,and the client TCP responds with an ACK. This is the first half of the TCP connection
termination.
3. The SIGCHLD signal is sent to the server parent and handled correctly.4. Nothing happens at the client. The client TCP receives the FIN from the server TCP
and responds with an ACK, but the problem is that the client process is blocked in the
call to fgets waiting for a line from the terminal.
5. We can find the state of the sockets.
6. We can still type a line of input to the client. Here is what happens at the clientstarting from Step 1:
8/22/2019 NPM Unit IIIfinal
14/30
MC9241-Network Programming
14
When we type "another line," str_cli calls writen and the client TCP sends the data to
the server. This is allowed by TCP because the receipt of the FIN by the client TCP only
indicates that the server process has closed its end of the connection and will not be
sending any more data. The receipt of the FIN does not tell the client TCP that the server
process has terminated. When the server TCP receives the data from the client, it
responds with an RST since the process that had that socket open has terminated.
7. The client process will not see the RST because it calls readline immediately after thecall to writen and readline returns 0 (EOF) immediately because of the FIN that was
received in Step 2. Our client is not expecting to receive an EOF at this point so it
quits with the error message "server terminated prematurely."
8. When the client terminates, all its open descriptors are closed.
Server host crashes:
What happens when the server host crashes?
To simulate this, we must run the client and server on different hosts. We then start
the server, start the client, type in a line to the client to verify that the connection is up,
disconnect the server host from the network, and type in another line at the client. This also
covers the scenario of the server host being unreachable when the client sends data.
The following steps take place:
1. We start the server and client and type one line to the client to verify that all is okay.We type in a line to the client to verify that the connection is up.
2. We disconnect the server host from the network to simulate the host crash. When theserver host crashes, nothing is sent out on the existing network connections. That is,
we are assuming the host crashes and is not shut down by an operator
8/22/2019 NPM Unit IIIfinal
15/30
MC9241-Network Programming
15
3. We type a line of input to the client, it is written by writen and is sent by the clientTCP as a data segment. The client then blocks in the call to readline, waiting for the
echoed reply.
4. Client TCP continually retransmits the data segment, trying to receive an ACK fromthe server. But there will not be any reply from the server, since it crashed. For Ex:
client retransmits the data segment 12 times, waiting for around 9 minutes before
giving up. When the client TCP finally gives 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 crashed and there were no responses at all to the client's
data segments, the error is ETIMEDOUT. But if some intermediate router
determined that the server host was unreachable and responded with an ICMP
"destination unreachable' message, the error is either EHOSTUNREACH or
ENETUNREACH.
Server crashes and reboots:
To simulate this scenario, we follow the preceding steps:
1. We start the server and then the client. Establish a connection between the client andserver. We type a line to verify that the connection is established.
2. The server host crashes and reboots. We simulate this by shutting down the server andrebooting it.
3. We type a line of input to the client, which is sent as a TCP data segment to the serverhost.
4. When the server host reboots after crashing, its TCP loses all information aboutconnections 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 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. 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.
8/22/2019 NPM Unit IIIfinal
16/30
MC9241-Network Programming
16
Server shutdown:
What happens if the server host is shut down by an operator while our server process is
running on that host?
When a Unix system is shut down, the following steps takes place;
3. The init process normally sends the SIGTERM signal to all processes4. waits some fixed amount of time (5-20 sec)5. Then sends the SIGKILL signal to any processes still running. This gives all
running processes a short amount of time to clean up and terminate. If we do
not catch SIGTERM and terminate, our server will be terminated by the SIGKILL
signal.
6. When the process terminates, all open descriptors are closed, and we thenfollow the same sequence of termination of server process.
I/O multiplexing:
In I/O multiplexing, input should be ready to be read or descriptor should be capable of
taking more outputs. Select() and poll() function is used in I/O multiplexing. I/O multiplexing
is typically used in networking applications in the following scenarios:
When a client is handling multiple descriptors When a client handles multiple sockets at the same time. If a TCP server handles both a listening socket and its connected sockets. If a server handles both TCP and UDP If a server handles multiple services and multiple protocols
I/O multiplexing is not limited to network programming. Many nontrivial applications
find a need for these techniques.
I/O models:
The basic differences in the five I/O models:
1. blocking I/O2. nonblocking I/O3. I/O multiplexing (select andpoll)4. signal driven I/O (SIGIO)5. asynchronous I/O (the POSIX aio_functions)
8/22/2019 NPM Unit IIIfinal
17/30
MC9241-Network Programming
17
There are normally two distinct phases for an input operation:
1. Waiting for the data to be ready2. Copying the data from the kernel to the process
1. Blocking I/O ModelThe most prevalent model for I/O is the blocking I/O model. By default, all sockets are
blocking. Using a datagram socket for our examples, we have the scenario shown in figure
below.
Figure - Blocking I/O model.
Here we refer to recvfrom as a system call because we are differentiating between our
application and the kernel. Regardless of how recvfrom is implemented, there is normally a
switch from running in the application to running in the kernel, followed at some time later
by a return to the application.
In above figure, 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. We say that our process is
8/22/2019 NPM Unit IIIfinal
18/30
MC9241-Network Programming
18
blocked the entire time from when it calls recvfrom until it returns. When recvfrom returns
successfully, our application processes the datagram.
2.
Nonblocking I/O ModelWhen we set a socket to be nonblocking, we are telling the kernel " when an I/O
operation that I request cannot be completed without putting the process to sleep, do
not put the process to sleep, but return an error instead."
The first three times that we call recvfrom, there is no data to return, so the kernelimmediately returns an error ofEWOULDBLOCKinstead.
The fourth time we call recvfrom, a datagram is ready, it is copied into our applicationbuffer, and
recvfromreturns successfully. We then process the data.
Figure - Nonblocking I/O model.
When an application sits in a loop calling recvfrom on a nonblocking descriptor likethis, 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.
8/22/2019 NPM Unit IIIfinal
19/30
MC9241-Network Programming
19
3. I/O Multiplexing ModelWith I/O multiplexing, we call select or poll and block in one of these two system calls,
instead of blocking in the actual I/O system call.
We block in a call to select, waiting for the datagram socket to be readable. When selectreturns that the socket is readable, we then call recvfrom to copy the datagram into our
application buffer.
Comparing I/O model with blocking model, there does not appear to be any
advantage, and in fact, there is a slight disadvantage because using select requires two system
calls instead of one.
Figure - I/O multiplexing model.
But the advantage in using select, which we will see later, is that 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
8/22/2019 NPM Unit IIIfinal
20/30
MC9241-Network Programming
20
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.
4.
Signal-Driven I/O ModelWe can also use signals, telling the kernel to notify us with the SIGIO signal when the
descriptor is ready. We call this signal-driven I/O.
We first enable the socket for signal-driven I/O and install a signal handler using the
sigactionsystem 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 read the datagram from the signal handler by calling recvfrom and then notify
the main loop that the data is ready to be processed, or we can notify the main loop and let it
read the datagram.
Figure - Signal-Driven I/O model.
Regardless of how we handle the signal, 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.
8/22/2019 NPM Unit IIIfinal
21/30
MC9241-Network Programming
21
5. Asynchronous I/O ModelAsynchronous I/O is defined by the POSIX specification, and various differences in the
real-time functions that appeared in the various standards which came together to form the
current POSIX specification have been reconciled. In general, these functions work by tellingthe 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 in the previous section 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.
The user call aio_read(the POSIX asynchronous I/O functions begin with aio_ or
lio_) and pass the kernel the descriptor, buffer pointer, buffer size (the same three arguments
for read), file offset (similar to lseek), and how to notify us when the entire operation is
complete.
Figure - Asynchronous I/O model.
This system call returns immediately and our process is not blocked while waiting for
the I/O to complete. We assume in this example that we ask the kernel to generate some
signal when the operation is complete. This signal is not generated until the data has been
copied into our application buffer, which is different from the signal-driven I/O model.
8/22/2019 NPM Unit IIIfinal
22/30
MC9241-Network Programming
22
Comparison of the I/O Models
The following figure is a comparison of the five different I/O models. It shows that 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.
Fig Comparison of the five I/O models.
Select function:
Select() - 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.
8/22/2019 NPM Unit IIIfinal
23/30
MC9241-Network Programming
23
Ex: we can call select and tell the kernel to return only when:
Any of the descriptors in the set {1, 4, 5} are ready for reading Any of the descriptors in the set {2, 7} are ready for writing Any of the descriptors in the set {1, 4} have an exception condition pending 10.2 seconds have elapsed
We tell the kernel what descriptors we are interested in (for reading, writing, or an exception
condition) and how long to wait.
Syntax:
Arguments:
Maxfdp1: The maxfdp1 argument specifies the number of descriptors to be tested. Its value
is the maximum descriptor to be tested plus one (hence our name of maxfdp1). The
descriptors 0, 1, 2, up through and including maxfdp1
1 are tested.
The maxfdp1 argument forces us to calculate the largest descriptor that we are interested in
and then tell the kernel this value. For example, lets turns on the indicators for descriptors 1,
4, and 5, the maxfdp1 value is 6. The reason it is 6 and not 5 is that we are specifying the
number of descriptors, not the largest value, and descriptors start at 0.
Timeout: Tells the kernel how long to wait for one of the specified descriptors to become
ready. A timeval structure specifies the number of seconds and microseconds.
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
There are three possibilities to wait:
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const
struct timeval *timeout);Returns: positive count of ready descriptors, 0 on timeout, 1 on
error
8/22/2019 NPM Unit IIIfinal
24/30
MC9241-Network Programming
24
1. Wait foreverReturn only when one of the specified descriptors is ready for I/O.For this, we specify the timeout argument as a null pointer.
2. Wait up to a fixed amount of timeReturn when one of the specified descriptors isready for I/O, but do not wait beyond the number of seconds and microseconds
specified in the timeval structure pointed to by the timeout argument.
3. Do not wait at allReturn immediately after checking the descriptors. This is calledpolling. To specify this, the timeout argument must point to a timeval structure and
the timer value must be 0.
Const: The const qualifier on the timeout argument means it is not modified by select on
return. For example, if we specify a time limit of 10 seconds, and select returns before the
timer expires with one or more of the descriptors ready or with an error ofEINTR, the timeval
structure is not updated with the number of seconds remaining when the function returns.
Readset, Writeset, Exceptset:
The three middle arguments, readset, writeset, and exceptset, specify the descriptors
that we want the kernel to test for reading, writing, and exception conditions.
There are only two exception conditions currently supported:
1. The arrival of out-of-band data for a socket.2. The presence of control status information to be read from the master side of a
pseudo-terminal that has been put into packet mode.
select()uses descriptor sets, typically an array of integers to a descriptor. All the
implementation details are hidden in the fd_set datatype and the following four macros:
We allocate a descriptor set of the fd_set datatype.
void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */
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 */
int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */
8/22/2019 NPM Unit IIIfinal
25/30
MC9241-Network Programming
25
For example, to define a variable of type fd_set and then turn on the bits for descriptors 1, 4,
and 5, we write
fd_set rset;
FD_ZERO(&rset); /* initialize the set: all bits off */
FD_SET(1, &rset); /* turn on bit for fd 1 */
FD_SET(4, &rset); /* turn on bit for fd 4 */
FD_SET(5, &rset); /* turn on bit for fd 5 */
Return Value: The return value from this function indicates the total number of bits that are
ready across all the descriptor sets. If the timer value expires before any of the descriptors are
ready, a value of 0 is returned. A return value of1 indicates an error.
Under What Conditions Is a Descriptor Ready?
1. A socket is ready for reading if any of the following four conditions is true:a. 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.
b. The read half of the connection is closedc. The socket is a listening socket and the number of completed connections is
nonzero.
d. A socket error is pending.
2. A socket is ready for writing if any of the following four conditions is true:a. 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
b. The write half of the connection is closedc. A socket using a non-blocking connect has completed the connection, or the
connect has failed.
d. A socket error is pending.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.
8/22/2019 NPM Unit IIIfinal
26/30
MC9241-Network Programming
26
Figure - Summary of conditions that cause a socket to be ready for select.
Shutdown function:
The normal way to terminate a network connection is to call the close function. But, there
are two limitations with close that can be avoided with shutdown:
1. close() decrements the descriptor's reference count and closes the socket only if thecount reaches 0. With shutdown, we can initiate TCP's normal connection termination
sequence, regardless of the reference count.
2. close() terminates both directions of data transfer, reading and writing. Since a TCPconnection is full-duplex, there are times when we want to tell the other end that we
have finished sending, even though that end might have more data to send us. This is
the scenario we encountered in the previous section with batch input to our str_cli
function.
The following figure is the typical function calls in this scenario.
Fig: Calling shutdown to close half of a TCP connection.
8/22/2019 NPM Unit IIIfinal
27/30
MC9241-Network Programming
27
Syntax for shutdown:
The action of the function depends on the value of the howto argument.SHUT_RD: The read half of the connection is closedNo more data can be received on
the socket and any data currently in the socket receive buffer is discarded. The
process can no longer issue any of the read functions on the socket. Any data
received after this call for a TCP socket is acknowledged and then silently
discarded.
By default, everything written to a routing socket loops back as possible input
to all routing sockets on the host. Some programs call shutdown with a secondargument of SHUT_RD to prevent the loopback copy. An alternative way to
prevent this loopback copy is to clear the SO_USEL
SHUT_WR: The write half of the connection is closedIn the case of TCP, this is called
a half-close. Any data currently in the socket send buffer will be sent,
followed by TCP's normal connection termination sequence. As we
mentioned earlier, this closing of the write half is done regardless of whether
or not the socket descriptor's reference count is currently greater than 0. The
int shutdown(int sockfd, int howto);
Returns: 0 if OK,1 on error
8/22/2019 NPM Unit IIIfinal
28/30
MC9241-Network Programming
28
SHUT_RD: The read half of the connection is closedNo more data can be received on
the socket and any data currently in the socket receive buffer is discarded. The
process can no longer issue any of the read functions on the socket. Any data
received after this call for a TCP socket is acknowledged and then silentlydiscarded.
By default, everything written to a routing socket loops back as possible input
to all routing sockets on the host. Some programs call shutdown with a second
argument of SHUT_RD to prevent the loopback copy. An alternative way to
prevent this loopback copy is to clear the SO_USEL
process can no longer issue any of the write functions on the socket.
SHUT_RDWR: The read half and the write half of the connection are both closedThis is
equivalent to calling shutdown twice: first with SHUT_RD and then with
SHUT_WR.
Poll function:
The Poll function was originally limited to STREAMS devices. Poll provides
functionality that is similar to select, but poll provides additional information when dealing
with STREAMS devices.
Syntax for Poll:
Arguments:
*fdarray - Pointer to the first element of an array of structures. Each element of the array is a
pollfd structure that specifies the following:.
struct pollfd {
int fd; /* descriptor to check */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
#include
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);
Returns: count of ready descriptors, 0 on timeout, 1 on
error
8/22/2019 NPM Unit IIIfinal
29/30
MC9241-Network Programming
29
};
FdFunction Descriptor,
Events - The conditions to be tested are specified.
Revents - returns the status for that descriptor in the corresponding revents member.
The following figure shows the constants used to specify the events flag and to test the revents
flag:
We have divided this figure into three sections: The first four constants deal with input, the
next three deal with output, and the final three deal with errors. Notice that the final three
cannot be set in events, but are always returned in revents when the corresponding condition
exists.
Figure - Input events and returned revents for poll.
There are three classes of data identified by poll: normal, priority band, and high-priority.
The following conditions causepoll to return the specified revent.
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 The presence of an error for a TCP connection can be considered either normal data
or an error (POLLERR).
8/22/2019 NPM Unit IIIfinal
30/30
MC9241-Network Programming
The availability of a new connection on a listening socket can be considered eithernormal data or priority data.
The completion of a nonblocking connect is considered to make a socket writable.
Nfds: The number of elements in the array of structures
Timeout: How long the function is to wait before returning. A positive value specifies the
number of milliseconds to wait. The following figure shows the possible values for the
timeout argument.
Figure - timeout values for poll.
INFTIM: Negative value.
Return value: The return value from poll is 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.