Advanced socket programming

Socket Options POSIX signal handling Signal-driven I/O Advanced I/O functions It‘s important to know about some of these topics, although it might not be apparent how and when to use them.


What kinds of options that we can set to a socket?
Socket options
related to the behavior of sockets and network protocols

Socket Options
Various attributes that are used to determine the behavior of sockets
packet delay, buffer size, socket close procedure,…

File options
related to “how to handle data input/output”

Setting options tells the OS/Protocol Stack the behavior we want. Support for generic options (apply to all sockets) and protocol specific options.



Option types
Many socket options are Boolean flags indicating whether some feature is enabled (1) or disabled (0).
… 1 0 1 0 1 0 …

Setting and Getting option values
getsockopt() gets the current value of a socket option. setsockopt() is used to set the value of a socket option. #include <sys/socket.h>

Other options are associated with more complex types including int, timeval, in_addr, sockaddr, etc.




int getsockopt( int sockfd, int level, int optname, void *opval, socklen_t *optlen);
sockfd: refers to an open socket descriptor. level: specifies whether the option is a general option or a protocol specific option (what level of code should interpret the option). optname: an integer that specifies the option optval: a pointer to a variable into which the current value of the option is stored by getsockopt. optlen: the size of the above variable as a value-result.

int setsockopt( int sockfd, int level, int optname, const void *opval, socklen_t optlen);
optval: a pointer to a variable from which the new value of the option is fetched by setsockopt optlen: the size of the above variable as a value for setsockopt .



General Options
Protocol independent options, applying to all kinds of sockets
Some general options are supported only by specific types of sockets (SOCK_DGRAM, SOCK_STREAM).

Some Generic Options

Handled by the generic socket system code.



Boolean option: enables/disables sending of broadcast messages. Underlying DL layer must support broadcasting! Can not applies to SOCK_STREAM sockets. Prevents applications from inadvertently sending broadcasts (OS looks for this flag when broadcast address is specified).

Example of using SO_BROADCAST option




Boolean option: enables bypassing of normal routing.
E.g. Packets are directed to the appropriate local interface

Integer value option: indicate an error that occurs on a socket
a variable named so_error is set for that socket Process is notified of the error in one of two ways: select call returns or the generation of the SIGIO signal

Used by routing daemons (e.g. routed and gated).

The process can then obtain the value of so_error by fetching the SO_ERROR socket option Readable (get’able) only!



Boolean option: enabled means that STREAM sockets should send a probe to peer if no data flow for a “long time”. Used by TCP - allows a process to determine whether peer process/host has crashed. Consider what would happen to an open telnet connection without keepalive.

Value is of type:
struct linger {
int l_onoff; int l_linger; /* 0 = off */ /* time in seconds */


Used to control whether and how long a call to close will wait for pending ACKS. connection-oriented sockets only.



Integer values options - change the receive and send buffer sizes. Can be used with STREAM and DGRAM sockets. With TCP, this option effects the window size used for flow control - must be established before connection is made.

Boolean option: enables binding to an address (port) that is already in use. Used by servers that are transient - allows binding a passive socket to a port currently in use (with active sockets) by other processes.





Allows a listening server to start and bind its well-known port, even if previously established connections that use this port exist Can be used to establish separate servers for the same service on different interfaces (or different IP addresses on the same interface) Allows completely duplicate bindings: a bind of an IP address and port
supported only for UDP sockets and used with multicasting

IP Options (IPv4)
IP_HDRINCL: used on raw IP sockets when we want to build the IP header ourselves. IP_TOS: allows us to set the “Type-ofservice” field in an IP header. IP_TTL: allows us to set the “Time-to-live” field in an IP header.



TCP socket options
TCP_KEEPALIVE: set the idle time used when SO_KEEPALIVE is enabled. TCP_MAXSEG: set the maximum segment size sent by a TCP socket.

Another TCP socket option
TCP_NODELAY: can disable TCP’s Nagle algorithm that delays sending small packets if there is unACK’d data pending. TCP_NODELAY also disables delayed ACKS (TCP ACKs are cumulative).



fcntl Function
stands for "file control" provides the following features related to network programming: Nonblocking I/O— We can set the O_NONBLOCK file status flag using the F_SETFL command to set a socket as nonblocking Signal-driven I/O— We can set the O_ASYNC file status flag using the F_SETFL command, which causes the SIGIO signal to be generated when the status of a socket changes The F_SETOWN command lets us set the socket owner (the process ID or process group ID) to receive the SIGIO and SIGURG signals

fcntl Function (2)
#include <fcntl.h> int fcntl(int fd, int cmd, ... /* int arg */ ); Returns: depends on cmd if OK, -1 on error

Each descriptor (including a socket) has a set of file flags that is fetched with the F_GETFL command and set with the F_SETFL command. • The two flags that affect a socket are
O_NONBLOCK—nonblocking I/O O_ASYNC—signal-driven I/O




Example of using fcntl
Codes to enable nonblocking I/O, using fcntl:
int flags; /* Set a socket as nonblocking */ if ( (flags = fcntl (fd, F_GETFL, 0)) < 0) err_sys("F_GETFL error"); flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) err_sys("F_SETFL error");

ioctl Function
have traditionally been the system interface used for everything that didn't fit into some other nicely defined category A common use of ioctl by network programs (typically servers) is to obtain information on all the host's interfaces when the program starts:
the interface addresses whether the interface supports broadcasting whether the interface supports multicasting …

not this one
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) err_sys("F_SETFL error");


ioctl Function
#include <unistd.h> int ioctl(int fd, int request, ... /* void *arg */ ); Returns:0 if OK, -1 on error

Socket Options Summary
This was just an overview
there are many details associated with the options described. There are many options that haven’t been described. Our text is one of the best sources of information about socket options.

the requests related to networking can be divided into six categories:
Socket operations File operations Interface operations ARP cache operations Routing table operations STREAMS system



POSIX Signal Handling

Sometime the operating system wants to tell a process about an event
I/O event, process termination, hardware error, …

Hey, I have something for you process





Introduction (2)
Sometime one process wants to communicate something to another process in a hurry or to itself

A notification to a process that an event has occurred
sometimes called software interrupts

Occur asynchronously
A process doesn't know ahead of time exactly when a signal will occur
Send to my future



Who sends a signal?
Signals can be sent
By one process to another process (or to itself) By the kernel to a process

How to deal with a signal?
Every signal has a disposition, which is also called the action associated with the signal. We set the disposition of a signal by calling the sigaction function



Three choices for the disposition
Provide a function that is called whenever a specific signal occurs
The function is called with a single integer argument that is the signal number and the function returns nothing void handler (int signo); A few signals,SIGIO, SIGPOLL, and SIGURG, require additional actions on the part of the process to catch the signal. The two signals SIGKILL and SIGSTOP cannot be caught.

sigaction function
int sigaction (int signum, const struct sigaction *act, struct sigaction *oact)
signum: specifies the signal except SIGKILL, SIGSTOP act: the new action for the signal signo (if act is not null) oact: saves the previous action for signal signo (if oact is not null)

Ignore a signal by setting its disposition to SIG_IGN.
The two signals SIGKILL and SIGSTOP cannot be ignored.

Set the default disposition for a signal by setting its disposition to SIG_DFL.
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: SIGCHLD and SIGURG





The sigaction structure
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); } sa_handler specifies the action to be associated with signum and or a pointer to a signal handling function.
This function receives the signal number as its only argument.

signal function
An easier way to set the disposition of a signal but there are different implementations
#include "unp.h" Sigfunc * signal(int signo, Sigfunc *func){ struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); }

sa_sigaction also specifies the action to be associated with signum but it has different arguments sa_mask gives a mask of signals which should be blocked during execution of the signal handler. sa_flags specifies a set of flags which modify the behaviour of the signal handling process. sa_restorer: not be used.


Signal-driven I/O model

Signal-driven I/O
process continues executing

application establish SIGIO signal handler sigaction system call return


signal handler recvfrom

deliver SIGIO system call datagram ready copy datagram

wait for data

process blocks while data is copied into application buffer

return OK process datagram copy complete

copy data from kernel to user



Steps to realize signal-Driven I/O
A signal handler must be established for the SIGIO signal. The socket owner must be set, normally with the F_SETOWN command of fcntl Signal-driven I/O must be enabled for the socket, normally with the F_SETFL command of fcntl to turn on the O_ASYNC flag

SIGIO with UDP Sockets
The SIGIO signal is generated whenever
A datagram arrives for the socket An asynchronous error occurs on the socket





SIGIO with TCP Sockets
Numerous conditions can cause SIGIO
A connection request has completed on a listening socket A disconnect request has been initiated A disconnect request has completed Half of a connection has been shut down Data has arrived on a socket Data has been sent from a socket (i.e., the output buffer has free space) An asynchronous error occurred

UDP Echo Server Using SIGIO
Put received data in a queue of datagrams to process After handling SIGIO, increase a queue counter nqueue Echo received data when a queue counter is not 0 What would happen if SIGIO was not blocked while we tested the variable nqueue ?
43 44

=> Little use

Signal-driven I/O has the kernel notify us with the SIGIO signal when "something" happens on a socket.
With a connected TCP socket, numerous conditions can cause this notification, making this feature of little use. With a listening TCP socket, this notification occurs when a new connection is ready to be accepted. With UDP, this notification means either a datagram has arrived or an asynchronous error has arrived
In both cases, we call recvfrom.

Advanced I/O functions



We can handle socket I/O with read(), write() but…
We have no options

Three variations on read() and write()
recv() and send()
allow a fourth argument that contains flags from the process to the kernel

We may need
setting timeout on a socket I/O call a socket not to be blocked with socket I/O to read into or write from one or more buffers the size of queuing data …

readv() and writev()
let us specify a vector of buffers to input into or output from

recvmsg() and sendmsg()
combine all the features from the other I/O functions



Socket Timeouts
How to place a timeout on an I/O operation
Call alarm, which generates the SIGALRM signal when the specified time has expired Block waiting for I/O in select, which has a time limit built-in Use the newer SO_RCVTIMEO and SO_SNDTIMEO socket options

socket I/O with a Timeout Using SIGALRM
set an SIGALRM and establish a signal handler for SIGALRM
alarm clock turn off alarm I/O socket call return alarm clock

call a socket I/O set an alarm clock for the process

call a socket I/O set an alarm clock for the process

handle SIGALRM

connect with a timeout using SIGALRM

recvfrom with a Timeout Using SIGALRM



recvfrom with a Timeout Using select
#include <sys/select.h>#include <sys/time.h> 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

readable_timeo function: waits for a descriptor to become readable

We can set a timeout within a select() call





dg_cli function that calls readable_timeo to set a timeout

recvfrom with a Timeout Using the SO_RCVTIMEO Socket Option
SO_RCVTIMEO socket options
allow us to place a timeout on socket receives the argument to the sockopt functions is a pointer to a timeval structure set once, apply to all



dg_cli function that uses the SO_RCVTIMEO socket option to set a timeout.

Problems with timeout setting
How to set the amount of timeout ?
the delay time between two peer hosts the processing time at the server …

How to deal with a timeout?
restart the communication? or send a message again?



recv and send Functions
#include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags); Both return: number of bytes read or written if OK, –1 on error

readv and writev Functions
Allow us to read into or write from one or more buffers with a single function call Can be used with any descriptor
#include <sys/uio.h> ssize_t readv(int fd, const struct iovec *iov, int iovcnt); ssize_t writev(int fd, const struct iovec *iov, int iovcnt); Both return: number of bytes read or written, –1 on error

flags for I/O functions
MSG_DONTROUTE: bypass routing table lookup MSG_DONTWAIT: specifies nonblocking for a single I/O operation MSG_OOB: send or receive out-of-band data MSG_PEEK: peek an incoming message MSG_WAITALL: wait for all data

struct iovec { void *iov_base; /* starting address of buffer */ size_t iov_len; /* size of buffer */ };



recvmsg and sendmsg Functions
the most general of all the I/O functions
#include <sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags); Both return: number of bytes read or written if OK, –1 on error struct msghdr { void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; socklen_t msg_controllen; int msg_flags; };

/* protocol address */ /* size of protocol address */ /* scatter/gather array */ /* # elements in msg_iov */ /* ancillary data (cmsghdr struct) */ /* length of ancillary data */ /* flags returned by recvmsg() */