Understanding inet_ntoa in Sockets
Understanding inet_ntoa in Sockets
Module -2
Sockets Introduction – socket address structures, value-result arguments, byte ordering and
manipulation functions, address conversion functions, Elementary TCP Sockets – socket,
connect, bind, listen, accept, fork and concurrent server design, getcsockname and
getpeername functions and TCP Client/Server Example.
Sockets Introduction
Most network application can be divided into two programs: client and server with the
communication link between them as shown:
Examples are: A web browser communicating with a web server. A FTP client fetching a
file form an FTP server etc. A client normally communicates with a server at a time.
However, a server is likely to communicate with multiple client. The client and server
communication with in the same Ethernet and the communication when LAN connected
through WAN is shown below.
Dept of CSE 1
Module-2 Network Programming
As seen in above figure, TCP and IP protocols are normally part of the protocol stack within
the kernel. In addition to TCP and IP, other protocol like UDP is also used. IP that was in
use since early 1980 is called as IP version 4 (Ipv4). A new version IP version 6 (Ipv6) is
being used since mid-1990.
Socket Address Structure SAS:
This SAS is between application and kernal. An address conversion function translates
between text representation of an address and binary value that makes up SAS. IPv4 uses
inet_addr and inet_ntoa. But inet_pton and inet_ntop handle IPv4 and IPv6. Above
functiuons are protocol dependent. However the functions starting with sock are protocol
independent. Most socket functions require a pointer to a socket address structure as an
argument.
IPv4 Socket AS: It is defined as follows:
# include <netinet / in.h>
sin_len, added in 4.3 BSD, is not normally supported by many vendors. It facilitates
handling of variable length socket address structures.
Various data types that are commonly used are listed below:
Dept of CSE 2
Module-2 Network Programming
Length field is never used and set. It is used within the kernal before routines that deal
with socket address structures from various protocol families.
Four socket functions – bind(), connect(), sendto(), sendmsg() -pass socket address
structures from application to kernal. All invoke sockargs() in Berkley derived
implementation. This function copies socket address structures and explicitly set
the sin_len member to the size of the structure that was passed. The other socket functions
that pass socket address to the application from kernal accept(), recvfrom(), recvmsg(),
getpeername() and getsockname() all set the sin_len member before returning to the
process.
sin_port, sin_family and sin_addr are the only required for
Posix.1g. sin_zero is implemented to keep the structure length to 16 byte.
Generic Socket Address Structure.
Socket address structures are always passed by reference when passed as an arguments to
any of the socket functions.
int bind (int sockfd, struct sockaddr *, socklen_t);
But the socket function that accept these address structures as pointers must deal with any
of these supported protocols. This calls for the any functions must cast the pointer to the
protocol specific socket address structure to be a pointer to a generic socket address
structure. For example
struct sockaddr_in serv;
bind (sockfd, (struct sockaddr *) &serv, sizeof(serv));
If we omit the cast, the C compiler generates a warning of the form incompatible pointer
type.
Different socket address structures are :
Dept of CSE 3
Module-2 Network Programming
IPv4 (24 bytes), IPv6 (24 bytes), Unix variable length and Data link variable length.
IPv6 SAS:
Defined by # include<netinet/in.h> header. The structure is shown below:
• The sin6_flowinfo member is divided into three fields o The low order 24 bits are the
flow label
o The next 4 bits are the priority o The next 4 bits are reserved.
Dept of CSE 4
Module-2 Network Programming
Dept of CSE 5
Module-2 Network Programming
Since the kernal is passed both pointer and the size of what the pointer points to, it knows
exactly how much data to copy from the process into the kernal. Following figures shows
this scenario:
The four functions accept(), recvmsg(), getsockname() and getpeername() pass a socket
address structure from kernal to the process, the reverse direction form the precious
scenario. In this case the length is passed as pointer to an integer containing the size of
structure as in
struct sockaddr_un cli; // Unix domain// socklen_t len;
len = sizeof (cli);
getpeername (unixfd, (SA *) &cli, & len );
The reason that the size changes from an integer to be a pointer to an integer is because
the size is both value when the function is called ( it tells the kernal the size of the structure
so that the kernal does not write past the end of the4 structure when filling it ) and it is
the result when the function results (It tells the process how much information the kernal
actually stored in the structure). This type of argument is called value – result arguments.
Dept of CSE 6
Module-2 Network Programming
With variable length socket address structure, the value returned can be less than the
maximum size of the structure.
Byte Order functions
There are two ways to store the 2 bytes in the memory. With the low order byte at the
starting address known little endian byte order a or with high order byte at the starting
address known as big endian byte order.
The terms little endian or big endian indicate which end of the multi byte value , the
little end or the big end is stored at the starting address of the value. Different systems
use different orderings. For example, PowerPC of IB, sparc of Sun Soloaris, Happal of HP
all use big endian while i386 PC of BSDi and i586 pc of Linux use little endian. The byte
Dept of CSE 7
Module-2 Network Programming
order used by a given system is known as the host byte order. As network programmer,
one need to deal with the different byte orders. That is the sending and receiving protocol
stack must agree on the order in which the bytes of these multibyte fields are transmitted.
Internet protocol uses big endian byte ordering for these multibyte system.
Normally, the address structure may be maintained in the host byte order system. Then it
may be converted into network byte order as per requirement. However, Posix.1g specifies
that the certain files in socket address structure be maintained in network byte order .
Therefore, there are function that convert between these two byte orders.
# include <netinet/in.h>
Appropriate function are called to convert a given value between the host and network byte
order. On those systems that have the same byte order as the Internet protocols (big endian),
these four functions are usually defined as null macros.
Dept of CSE 8
Module-2 Network Programming
The first group of functions whose name begin with b (for byte) are from 4.3. BSD.
The second group of functions whose name begin with mem ( for memory) are from ANSI
C library.
First Berkeley derived functions are shown.
# include <strings.h>
void bzero ( void *dest, size_t nbytes);
void bcpy (const void *src, void * dest, size_t nbytes);
int bcmp ( cost void * ptr1, const void *ptr2, size_t nbytes)
constant qualifier indicates that the pointer with this qualification, src, ptr1, ptr2 are not
modified by the function.. That is memory pointed to by the cost pointer is read but not
modified by the function.
bzero ( ) sets the specified number of bytes to 0 in the destination. This function is
often used to initialize a socket address structure to 0. bcopy ( ) moves the specified
number of bytes from the source to the destination. bcmp ( ) compares two arbitrary byte
strings . The return value is zero if the two byte strings are identical; otherwise it is nonzero.
Following are the ANSI C functions:
# include <strings.h>
void memset ( void *dest, int c, size_t len);
void memcpy (void *dest, const void * src, size_t nbytes); int memcmp ( const
void * ptr1, const void *ptr2, size_t
nbytes); Returns 0 if equal, <0 or >0 if unequal.
memset () sets the specified number of bytes to the value in c in the
destination, memcpy() is similar to bcopy () but the order of the two pointer arguments is
swapped. bcopy correctly handles overlapping fields, while the behaviour of memcpy() is
undefined if the source and destination overlap. memmove() functions can be used when
the fields overlap. memcpy() compares two arbitrary byte strings and returns 0 if they are
identical, if not, the return value is either greater than 0 or less than 0 depending whether
the first unequal byte pointed to by ptr1 is greater than or less than the corresponding byte
pointed to by ptr 2.
Address conversion functions: There are two groups of address conversion function that
convert the Internet address between ASCII strings (readable form) to network byte
ordered binary values and vice versa.
Dept of CSE 9
Module-2 Network Programming
Dept of CSE 10
Module-2 Network Programming
overflowing the caller‘s buffer. To help specify this size, following two definitions are
defined by including the
<netinet/in.h> header:
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
If LEN is too small to hold the resulting presentation format including the terminating
null, a null pointer is returned and errno is set ot ENOSPC.
The strptr argument to inet_ntop cannot be a null pointer. The caller must allocate
memory for the destination and specify its size. On success this pointer is the return value
of the function.
This is summarized in the following figure.
Elementary TCP Sockets
Socket calls are those functions that provide access to the underlying functionality and
utility routines that help the programmer. A socket can be used by client or by a server,
for a stream transfer (TCP) or datagram (UDP) communication with a specific endpoints
address.
Following figure shows a time line of the typical scenario that takes place between client
and server.
Dept of CSE 11
Module-2 Network Programming
First server is started, then sometimes later a client is started that connects to the
server. The client sends a request to the server, the server processes the request, and the
server sends back reply to the client. This continues until the client closes its end of the
connection, which sends an end of file notification to the server. The server then closes
its end of the connections and either terminates or waits for a new connection.
socket function:
#include socket (int family, int type, int protocol);
returns negative descriptor if OK & –1 on error.
Arguments specify the protocol family and the protocol or type of service it needs (stream
or datagram). The protocol argument is set to 0 except for raw sockets.
Dept of CSE 12
Module-2 Network Programming
Not all combinations of socket family and type are valid. Following figure shows the valid
combination.
2. In case for SYN request, a RST is returned (hard error), this indicates that no
process is waiting for connection on the server. In this
case ECONNREFUSED is returned to the client as soon the RST is received.
RST is received when (a) a SYN arrives for a port that has no listening server
Dept of CSE 13
Module-2 Network Programming
(b) when TCP wants to abort an existing connection, (c) when TCP receives a
segment for a connection does not exist.
3. If the SYN elicits an ICMP destination is unreachable from some
intermediate router, this is considered a soft error. The client server saves the
message but keeps sending SYN for the time period of 75 seconds. If no
response is received, ICMP error is returned as
EHOSTUNREACH or ENETUNREACH.
In terms of the TCP state transition diagram, connect() moves from the
CLOSED state to the SYN_SENT state and then on success to the
ESTABLISHED state. If the connect fails, the socket is no longer usable and must
be closed.
Bind(): When a socket is created, it does not have any notion of end points
addresses An application calls bind to specify the local endpoint address for a
socket. That is the bind function assigns a local port and address to a socket..
#include <sys/socket.h>
int bind (int sockfd, const strut sockaddr *myaddr, socklen_t addrlen)
The second arguments is a pointer to a protocol specific address and the third
argument is the size of this address structure. Server bind their well known port when they
start. (A TCP client does not bind an IP address to its socket.)
listen Function:
The listen function is called only by TCP server and it performs following
functions.
The listen function converts an unconnected socket into a passive socket, indicating
that the kernel should accept incoming connection requests directed to this socket. In
terms of TCP transmission diagram the call to listen moves the socket from
the CLOSED state to the LISTEN state.
The second argument to this function specifies the maximum number of
connections that the kernel should queue for this socket.
#include <sys/socket.h>
int listen (int sockfd, int backlog); returns 0 if OK -1 on error.
This function is normally called after both the socket and bind functions and must be
called before calling the accept function.
Dept of CSE 14
Module-2 Network Programming
The kernel maintains two queues and the backlog is the sum of these two queues. These
are :
An incomplete connection queue, which contains an entry for each SYN that has
arrived from a client for which the server is awaiting completion of the TCP three
way handshake. These sockets are in the SYN_RECD state.
A Completed Connection Queue which contains an entry for each client with whom
three handshake has completed. These sockets are in the ESTABLISHED state.
Following figure depicts these two queues for a given listening socket.
When a SYN arrives from a client, TCP creates a new entry on the incomplete queue and
then responds with the second segment of the three way handshake. The server ‘s SYN
with an ACK of the clients SYN. This entry will remain on the incomplete queue until the
third segment of the three way handshake arrives ( the client‘s ACK of the server‘s SYN)
or the entry times out. If the three way hand shake completes normally, the entry moves
from the incomplete queue to the completed queue. When the process calls accept, the
first entry on the completed queue is returned to the process or, if the queue is empty, the
process is put to sleep until an entry is placed onto the completed queue. If the queue are
full when a client arrives, TCP ignores the arriving SYN, it does not send an RST. This is
because the condition is considered temporary and the client TCP will retransmit its SYN
with the hope of finding room in the queue.
Dept of CSE 15
Module-2 Network Programming
accept Function : accept is called by a TCP server to return the next completed
connection from the from of the completed connection queue. If the completed queue is
empty, the process is put to sleep.
# include <sys/socket.h>
int accept ( sockfd, struct sockaddr * cliaddr, socklen_t *addrlen) ;
return non negative descriptor if OK, -1 on error.
The cliaddr and addrlen arguments are used to return the protocol address of the
connected peer process (the client). addrlen is a value-result argument before the call, we
set the integer value pointed to by *addrlen to the size of the socket address structure
pointed to by cliaddr and on return this integer value contains the actual number of bytes
stored by the kernel in the socket address structure. If accept is successful, its return value
is a brand new descriptor that was automatically created by the kernel. This new descriptor
refers to the TCP connection with the client. When discussing accept we call the first
argument to accept the listening and we call the return value from a accept the connected
socket
fork function:
fork is the function that enables the Unix to create a new process
#inlcude <unistd.h>
pid_t fork (void); Returns 0 in child, process ID of child in parent, -1 on error
There are two typical uses of fork function:
1. A process makes a copy of itself so that one copy can handle one operation while
the other copy does another task. This is normal way of working in a network
servers.
2. A process wants to execute another program. Since the only way to create a
new process is by calling fork, the process first calls fork to make a copy of
itself, and then one of the copies(typically the child process) calls exec function
Dept of CSE 16
Module-2 Network Programming
to replace itself with a the new program. This is typical for program such as
shells.
3. fork function although called once, it returns twice. It returns once in the
calling process (called the parent) with a return value that is process ID of the
newly created process (the child). It also returns once in the child, with a return
value of 0. Hence the return value
tells the process whether it is the parent or the child.
4. The reason fork returns 0 in the child, instead of parent‘s process ID is because
a child has only one parent and it can always obtain the parent‘s process ID by
calling getppid A parent, on the other hand, can have any number of children,
and there is no way to obtain
the process Ids of its children. If the parent wants to keep track of the process
Ids of all its children, it must record the return values form fork.
exec function :
The only way in which an executable program file on disk is executed by Unix is for an
existing process to call one of the six exec functions. exec replaces the current process
image with the new program file and this new program normally starts at the main
function. The process ID does not change. The process that calls the exec is the calling
process and the newly executed program as the new program.
The differences in the six exec functions are:
a. whether the program file to execute is specified by a file name or a pathname.
b. Whether the arguments to the new program are listed one by one or reference
through an array of pointers, and
c. Whether the environment of the calling process is passed to the new program
or whether a new environment is specified.
#include <unistd.h>
int execl (const char *pathname, const char arg 0, …/ (char *) 0 */); int execv
(const char *pathname, char *const argv[ ]);
Dept of CSE 17
Module-2 Network Programming
int execle (const char *pathname, const char *arg 0, ./ * (char *)0,char *const
envp[] */); int execve (const char *pathname, char *const arg [], char *const
envp[]);
int execlp (const char *filename, const char arg 0, …/ (char *) 0 */); int execvp
(const char *filename, char *const argv[]);
These functions return to the caller only if an error occurs. Otherwise control passes
to the start of the new program, normally the main function.
The relationship among these six functions is shown in the following figure
. Normally only execve is a system call within the kernal and the other five are
library functions that call execve.
1. The three functions in the top row specify each argument string as a separate
argument to the exec function, with a null pointer terminating the variable number
of arguments. The three functions in the second row have an argv array containing
the pointers to the argument strings. This argv array must contain a null pointer to
specify its end, since a count is not specified.
1. The two functions in the left column specify a filename argument. This is
converted into a pathname using current PATH environment variable. IF
the execlp (file, arg,.., 0)
2. filename argument to execlp or execvp contains a slash (/) anywhaere in the
string, the PATH variable is not used. The four functions in the right two columns
specify a fully qualified pathname arguments.
Dept of CSE 18
Module-2 Network Programming
3. The four functions in the left two column do no specify an explicit environment
pointer. Instead the current value of the external variable environ is used for
building an environment list that is passed to the new program. The two functions
in the right column specify an explicit environment list. The envp array of pointers
must be terminated by a null pointer.
concurrent server design
A server that handles a simple program such as daytime server is a iterative server. But
when the client request can take longer to service, the server should not be tied upto a single
client.
The server must be capable of handling multiple clients at the same time. The simplest way
to write a concurrent server under Unix is to fork a child process to handle each client.
Following program shows the typical concurrent server.
pid_t pid;
int listenfd, connfd;
listfd = socket ( , , , ); /*fill in sockaddr_in with server’s well known port*/ bind
(listenfd, …);
listen (listenfd, LISTENQ);
for ( ; ; ) {
connfd = accept (listenfd, …); if ( (pid = fork())== 0) {
}
close (connfd); /* parent closes connected socket*/
}
Dept of CSE 19
Module-2 Network Programming
When a connection is established , accept returns, the server calls fork, and
then the child process services the client ( on connfd, the connected socket ) and
the parent process waits for another connection ( on listenfd, the listening socket ).
The parent closes the connected socket since the child handles this new client.
In the above program, the function doit does whatever is required to service the client.
When this functions returns, we explicitly close the connected socket in the child. This is
not required since the next statement calls exit, and part of process termination is closing
all open descriptors by the kernal. Whether to include this explicit call to close or not is a
matter of personal programming taste.
The connection scene in case of concurrent server is shown below:
Dept of CSE 20
Module-2 Network Programming
close() The normal Unix close() is also used to close a socket and terminate a TCP
connection.
#include<unistd.h>
int close (int sockfd);
The default action of close with a TCP socket is to mark the socket as closed and
return to the process immediately. The socket descriptor is no longer usable by the
process. Thst is , it can not be used as an argument to read or write.
getsockname () and getpeername():
Dept of CSE 21
Module-2 Network Programming
These two functions return either the local protocol address associated with a socket
or the foreign address associated with a socket.
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localadddr, socklen_t
*addlen);
int getpeername(int sockfd, struct sockaddr *peeradddr, socklen_t
*addlen); both return 0 if OK and –1 on error.
These functions are required for the following reasons.
a. After connect successfully returns a TCP client that does not call bind(),
getsocketname() returns the local IP address and local port number assigned to the
connection by the kernel
b. After calling bind with a port number of 0, getsockname() returns the local port
number that was assigned
c. When the server is exceed by the process that calls accept(), the only way the
server can obtain the identity of the client is to call getpeername().
Functions fgets() and fputs() are from standard I/O library. And writen() and
readline() are function created by the W Richard Stevans (WRS) (code given in the
section 3.9)
Dept of CSE 22
Module-2 Network Programming
The communication between client and server is understood by Echo client and Server.
The Following code corresponds to Server side program.
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.
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.
Dept of CSE 23
Module-2 Network Programming
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
Dept of CSE 24
Module-2 Network Programming
Module-3
I/O Multiplexing and Socket Options-I/O Modules, select function, str_cli function, batch
input and buffering shutdown function, TCP Echo Server, pselect function, poll function.
• If a TCP server handles both a listening socket and its connected sockets, I / O
multiplexing is normally used.
• IF a server handles both TCP and UDP, I/O multiplexing is normally used.
Dept of CSE 25
Module-2 Network Programming
I/O Models:
There are five I /O models in the Unix. These are:
a. Blocking I /O
b. Non blocking I / O
c. I/O Multiplexing (select and poll)
d. Signal driven I/O (SIGIO)
e. Asynchronous I/O (the Posix 1 aio_functions)
Fo ran input operation on a socket the first step normally involves waiting for the
data to arrive on the network. When the packet arrives, it is copied into buffer within
the kernel. The second step is copying this data from the kernel‘s buffer into our
applications buffer.
The most prevalent model for I/O is the blocking I/O model, which we have used for all
our examples so far in the text.. BY default, all sockets are blocking. Using a datagram
socket for our examples we have the scenario as shown below. In UDP the concept of data
being ready to be read is simple because either an entire datagram packet is received or
not.
IN this example recvfrom as a system call as it differentiated between our application and
the kernel.
Dept of CSE 26
Module-2 Network Programming
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 blocked the entire time
from when it call recvfrom until it returns. When recvfrom returns OK, our application
processes the datagram.
Dept of CSE 27
Module-2 Network Programming
During the first three times, when the recvfrom is called, there is no data to return, so the
kernel immediately returns an error EWOULDBLOCK. Fourth time, when recvfrom is
called, the datagram is ready, it is copied into our application buffer and
the recvfrom returns OK. The application then process the data.
When the application puts the call recvfrom in a loop, on a non blocking descriptors like
this, it is called polling. The continuation polling of the kernel is waste of CPU time. But
this model is normally encountered on system that are dedicated to one function.
Signal Driven I/O Model
Signals are used to tell the kernel to notify applications with the SIGIO signal when
the descriptor is ready. It is called signal driven I/O Model. The summary is shown in
the following figure.
First enable the socket for the 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
continuous, it is not block. When the datagram is ready to be ready, the SIGIO signal is
generated for our process. We can either read the datagram from the signal handler by
Dept of CSE 28
Module-2 Network Programming
calling recvfrom and then notify the main loop that the data is ready to be process, or we
can notify the main lop and let it read the datagram.
The advantage I this model is that we are not blocked while waiting for the datagram top
arrive. The main loop can continue executing and just wait to be notified by the signal
handler that either the dta is read to process or that the datagram is ready to be read.
Asynchronous I/O are new with the 1993 edition of Posix 1g. In this the kernel is
told to start operation and to notify when the entire operation (including the copy of the
data from the kernel to our buffer ) is over.
The main difference between this and the signal driven I/O model in the previous
section is that with the signal driven I/O the kernel tells when the I/O operation
can be initiated. But with asynchronous I/O, the kernel tells us when an I/O
operation is complete. It is summarized as given below:
Dept of CSE 29
Module-2 Network Programming
The function aio_read is called and the descriptor, buffer pointer, buffer size, file
offset and how to notify when the entire operation is complete are passed to the kernel.
The system calls returns immediately and our process is not blocked waiting for the I./O
to complete. It is expected that the kernel will generate some signal when the operation is
complete. The signal is generated only when the data is copied completely in the
application buffer.
Comparison of the I/O Model
Dept of CSE 30
Module-2 Network Programming
The main difference between the four models is the first phase as the second phase
in the first four models is the same. The process is blocked in a cal to recvfrom while the
data is copied from the kernel to the caller‘s buffer. Asynchronous I/O however, handles
both the phases and is different form the first four.
Synchronous vs asynchronous:
A synchronous I/O operation causes the requesting process to be blocked until that
I?O operation is completes.
An asynchronous I/O operation does not cause the requesting process to be blocked.
Dept of CSE 31
Module-2 Network Programming
Based on this definition, the first four are synchronous as the actual I/O operation
blocks the process. Only asynchronous I/O model matches the asynchronous I/O
definition.
Select Function :
The select function allows the process to instruct the kernel to wait for any one of
the 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.
As an example, we can call select and tell the kernel to return only
• any of the descriptors in the set { 1,4,5} are ready for reading, or
• any of the descriptors in the set {2,7} are ready for writing, or
• any of the descriptors in the set {1,4 } have an exception condition pending or
• after 10.2. seconds have elapsed.
That the kernel is told in what descriptors we are interested in (for reading, writing or
an exception condition) and how long to wait. The descriptors in which we are
interested are not restricted to sockets: any descriptor can be tested using select.
int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *excepset, const
struct timeval *timeout); returns : positive count of ready descriptors, o on timeout, -1 on
error.
Consider the final arguments: This 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; /* micros seconds */
}
There are three possibilities:
• wait forever: return only when one of the specified descriptors is ready for I/O
. For this, we specify the timeout argument as a null pointer.
Dept of CSE 32
Module-2 Network Programming
• Wait upto a fixed amount of time: return when one of the specified descriptors
is ready for I/O, but do not wait beyond the number of seconds and microseconds
specified in the timeval structure pointer to by the timeout argument.
• Do not wait at all: return immediately after checking the descriptors. This is
called polling. To specify this, the timeout argument must point to a timeval
structure and the timer value must be zero
In case of first two scenario, the wait is normally interrupted if the process catches a
signal and returns from the signal handler.
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 sec, and select returns before the
timer expires, with one or more descriptors ready or with an error of EINTR, the
timeval structure is not updated with the number of seconds remaining when the
functions returns.
The three middle arguments readset, writeset and excepset specify the descriptors
that we want the kernel to test for reading, writing and exception conditions.
The two exceptions conditions currently supported are:
• The arrival of out of bound data for a socket.
• The control status information to be read from the master side of pseudo
terminal
The maxfdp1 argument specifies the number of descriptors to be tested. Its value is the
maximum descriptors to be tested plus one. The descriptors 0,1,2, through and
including maxfdp1 –1 are tested. The constant FD_SETSIZE defined by including <sys
/select.h>, is the number of descriptors in the fd_set
data type. It s value is often 1024, but few programs use that many descriptors.
The maxfdp1 argument forces us to calculate the largest descriptors that we are interested
in and then tell the kernel this value. For 1,4,5 the maxfdp1 is 6.
The design problem is how to specify one or more descriptors values for each of these three
arguments. Select uses descriptor sets, typically an array of integers – a32 by 32 array- of
1024 descriptors set. The descriptors are programmed for initializing /reading / writing and
for closing using the following macros:
Dept of CSE 33
Module-2 Network Programming
Select function modifies the descriptor sets pointed by the readset, writset and excepset
pointers. These three arguments are value-result arguments. When we call the function, we
specify the values of the descriptors that we are interested in and on return the result
indicates which descriptors are ready. We use FD_ISSET macros return to test a specific
descriptor in an fd_set structure. Any descriptors that is not ready on return will have its
corresponding bit cleared in the descriptors set.
str_cli Function :
The str_cli Function is rewritten using the select function as shown below:
# include ―unp.h‖
Dept of CSE 34
Module-2 Network Programming
fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];
FD_ZERO (&rset);
for ( ; ;)
{
FD_SET (fileno (fp), &rset);
FD_SET (sockfd, &rset);
maxfdp1 = max (fileno (fp), sockfd) + 1;
select (maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset))
{ if (readline (sockfd, recvline, MAXLINE)==0)
if (FD_ISSET(fileno(fp), &rset))
}
}
Dept of CSE 35
Module-2 Network Programming
IN the above code the descriptors set is initialized to zero using FD_ZERO. Then, the
descriptors, file pointer fp and socket sockfd are turned on using FD_SET. maxfdp1 is
calculated from the descriptors list. Select function is called. IN this writeset pointer and
exception set pointers are both NULL. Final time pointer is also NULL as the call is to be
blocked until something is ready.
If on return from the select, socket is readable, the echoed line is read
with readline and output by fputs.
If the standard input is readable, a line is read by fget sand written to the sockets
using writen.
IN this although the four functions fgets, writen, readline and fputs are used, the order of
flow within the function has changed. In this, instead of flow being driven by the call to
fgets, it is driven by the call to select.
This has enhanced the robustness of the client.
Batch Input:
The echo client server, works in a stop and wait mode. That is , it sends a line to the server
and then waits for the reply. This amount of time is one RTT (Round Trip Time) plus the
server‘s processing time. If we consider the network between the client and the server as a
full duplex pipe with requests from the client to server, and replies in the reverse direction,
then the following shows the stop and wait mode.
Dept of CSE 36
Module-2 Network Programming
The request is sent by the client at time 0 and we assume RTT of 8 units of time.
The reply sent at a time 4 is received at time 7. It is assumed that there is no serving
processing time and that the size of the request is the same as the reply. Also, TCP
acknowledgment are ignored.
But as there is a delay between sending a packet and that packet arriving at the
other end of the pipe, and since the pipe is full duplex, in this example we are only
using one- eighth of the pipe capacity.
This stop and wait mode is fine for interactive input, but since our client reads from
standard input and writes to standard output, we can easily run our client in a batch mode.
When the input is redirected as in client server example, the output file is always same.
To see what is happening in the batch mode, we can keep sending requests as fast as the
network can accept them. The server processes them and sends back the replies at the same
rate. This leads to the full pipe at time 7 as shown below:
Dept of CSE 37
Module-2 Network Programming
Filling the pipe between the client and the sever : batch Mode;
Now to understand the problem with the str_cli, let the input file contains only nine
lines. The last line is sent at time 8 as shown above. But we cannot close the connection
after writing this request, because there are still other requests and rep0les in the pipe. The
cause of the problem is our handling of end of file on input. The function returns to the
main function, which then terminates. But in a batch mode, an end of file on the input
does not imply. That we have finished reading from the socket. There might still requests
on the way to the server or replies on the way to back from the server.
Dept of CSE 38