Top Banner

of 30

NPM Unit IIIfinal

Aug 08, 2018

Download

Documents

monicadoss85
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
  • 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.