Network Programming and Management

UNIT II

Socket Options File Control I/O Control Signal Driven I/O

Server users will be connecting to Example #define MYPORT 3490 // the port
#define BACKLOG 10 // how many pending connections queue will hold int main(void) { int sockfd, new_fd; // listen on sockfd, new connection on new_fd struct sockaddr_in my_addr; // my address information struct sockaddr_in their_addr; // connector's address information int sin_size; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons(MYPORT); // short, network byte order my_addr.sin_addr.s_addr = INADDR_ANY; // auto. filled with local IP memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } while(1) { // main accept() loop sin_size = sizeof(struct sockaddr_in); if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) { perror("accept"); continue; } printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr)); if (send(new_fd, "Hello, world!\n", 14, 0) == -1) perror("send"); close(new_fd); } return 0; }

#include <netinet/in.h> #include <sys/socket.h>

Client Example
// the port client will be connecting to // max number of bytes we can get // at once

#define PORT 3490 #define MAXDATASIZE 100

int main(int argc, char *argv[]) { int sockfd, numbytes; char buf[MAXDATASIZE]; struct hostent *he; struct sockaddr_in their_addr;// server's address information if (argc != 2) { fprintf(stderr,"usage: client hostname\n"); exit(1); } if ((he=gethostbyname(argv[1])) == NULL) { // get the host info perror("gethostbyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); }

their_addr.sin_family = AF_INET; // host byte order their_addr.sin_port = htons(PORT); // short, network byte order their_addr.sin_addr = *((struct in_addr *)he->h_addr); // already network byte order memset(&(their_addr.sin_zero), '\0', 8); // zero the rest of the struct if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1){ perror("connect"); exit(1); } if ((numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { perror("recv"); exit(1); } buf[numbytes] = '\0'; printf("Received: %s",buf); close(sockfd); return 0; }

Signals
• Introduced in UNIX systems to simplify IPC. • Signal is a software interrupt • Used by the kernel to notify processes of system events or can be used for inter proccess communication. • A signal is a short message sent to a process, or group of processes, containing the number identifying the signal. – No data is delivered with traditional signals. – POSIX.4 defines i/f for queueing & ordering RT signals arguments.

• Signal sending: – Kernel updates descriptor of destination process. • Signal receiving: – Kernel forces target process to “handle” signal. • Pending signals are sent but not yet received. – Up to one pending signal per type for each process, except for POSIX.4 signals. – Subsequent signals are discarded. – Signals can be blocked, i.e., prevented from being received.

Signal Transmission

Signal-Related Data Structures
• sigset_t stores array of signals sent to a process. • The process descriptor (struct task_struct in <linux/sched.h>) has several fields for tracking sent, blocked and pending signals.
Signal handling system call : struct sigaction { void (*sa_handler)();/* handler address, or SIG_IGN(ignore), or SIG_DFL(default) */ sigset_t sa_mask; int sa_flags; } /* blocked signal list */ /* options e.g., SA_RESTART */

Signal Disposition
Disposition : Action associated with signals

• Ignore the signal (most signals can simply be ignored, except SIGKILL and SIGSTOP) • Handle the signal disposition via a signal handler routine. This allows us to gracefully shutdown a program when the user presses Ctrl-C (SIGINT). • Block the signal. In this case, the OS queues signals for possible later delivery • Let the default apply SIG_DFL (usually process termination) • Signals can be ignored SIG_IGN

Original Signal Handling (Version 7)
• Two includes: <sys/types.h> and <signal.h> • handler can either be: – a function (that takes a single int which is the signal) – the constant SIG_IGN – the constant SIG_DFL • signal will return SIG_ERR in case of error

Signal Handling System Calls
• int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
– Replaces the old signal() function. – Used to bind a handler to a signal. – For RT signals, the handler’s prototype is of form:
• void (*sa_sigaction)(int, siginfo_t *, void *);

Common Signals
• SIGHUP (1):sent to a process when its controlling terminal has disconnected • SIGINT (2) : Ctrl-C (or DELETE key) • SIGQUIT (3):Ctrl-\ (default produces core) • SIGSEGV (11):Segmentation fault • SIGKILL (4):Illegal instruction (default core) • SIGUSR[1,2]:User-defined signals (10,12) • kill –l will list all signals • SIGFPE (8):Floating Point Exception (divide by 0; integer overflow; floating-point underflow)

Alarming Signals
• SIGALRM can be used as a kind of “alarm clock” for a process • By setting a disposition for SIGALRM, a process can set an alarm to go off in x seconds with the call: – unsigned int alarm(unsigned int numseconds) • Alarms can be interrupted by other signals • Examples: mysleep.c, impatient.c

• • • • •

SIGCHLD signals – Sent by kernel when a process terminates to the parent of the child When Child is in zombie state it maintains information about the child termination status, process ID… to the parent to be fetched later. wait - wait for a child process to stop or terminate. Returns process ID and status of terminated child #include <sys/wait.h> pid_t wait(int *stat_loc); - waits untill first child terminates pid_t waitpid(pid_t pid, int *stat_loc, int options); - specifies process ID we want to wait for WNOHANG option tells waitpid not to block if there exist running children that have not yet terminated -Difference between wait() and waitpid(): It is used since unix signals are not queued. Can be used when the same client has multiple (n) connections with the concurrent server and when the child terminates n FIN signals sent to child process which responds by sending n SIGCHLD to parent.As unix signals are not queued the parent leaves zombie child if wait is used. Hence waitpid must be used within loop

I/O Blocking
socket(); bind() ; listen();

while
accept(); recv() ; send() ;

• Simple server has blocking problem
– – – – Suppose 5 connections accepted. Suppose next accept() blocks. Other connections cannot send and receive. Cannot get keyboard input either.

select() :I/O Multiplexing
• waits on multiple file descriptors and timeout • returns when any file descriptor
– is ready to be read or – written or – indicate an error, or – timeout exceeded

• advantages
– simple – application does not consume CPU cycles while waiting

• disadvantages
– does not scale to large number of file descriptors

Example: Server Programming
• • • • create stream socket (socket() ) Bind port to socket (bind() ) Listen for new client (listen() ) While

– Wait for (select() ) (depending on which file descriptors are ready) – accept user connection and create a new socket (accept() ) – data arrives from client (recv() ) – data has to be send to client (send() )

Server: Alternative Ways of Handling Many Clients
• Forking a new process for each client: fork() • But, creating new process is expensive. • Multithreaded implementation: have one thread handling each client. • Thread is like a process but lightweighted.

Network Programmer’s Mistakes
• • • • • byte ordering separating records in streams use of select() misinterpreting the project specification not knowing all available system calls

There are more System Calls
• Depends on communication type
– Datagram sockets use recvfrom() and sendto() for receiving and sending data

• Closing connection: close(), shutdown() • Convenient functions (on UNIX)
– inet_aton, inet_ntoa – inet_pton, inet_ntop

inet_ntop, inet_pton –
• • convert IPv4 and IPv6 addresses between binary and text form #include <arpa/inet.h>

• •

int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
af : Specifies the family of the address to be converted. AF_INET and AF_INET6 . src – (Input) The pointer to the null-terminated character string that contains the text presentation form of an IPv4 / IPV6 address. dst – (Output) The pointer to a buffer into which the function stores the numeric address. The calling application must ensure that the buffer referred to by dst is large enough to hold the numeric address (4 bytes for AF_INET or 16 bytes for AF_INET6). size • (Input) The size of the buffer pointed at by dst.

• •

• sock_ntop
- takes a pointer to a socket address structure, looks
inside the structure, and calls the appropriate function to return the presentation format of the address.

• #include "unp.h“
• char *sock_ntop(const struct sockaddr *sockaddr, socklen_t addrlen);
• • • Returns: non-null pointer if OK, NULL on error sockaddr points to a socket address structure whose length is addrlen. The function uses its own static buffer to hold the result and a pointer to this buffer is the return value.

readn, writen, and readline Functions
• • • • A read or write on a stream socket might input or output fewer bytes than requested. The reason is that buffer limits might be reached for the socket in the kernel. All that is required to input or output the remaining bytes is for the caller to invoke the read or write function again. This scenario is always a possibility on a stream socket with read, but is normally seen with write only if the socket is nonblocking. #include "unp.h“

• ssize_t readn(int filedes, void *buff, size_t nbytes); • ssize_t writen(int filedes, const void *buff, size_t nbytes); • ssize_t readline(int filedes, void *buff, size_t maxlen);
• • All return: number of bytes read or written, –1 on error Note that our readline function calls the system’s read function once for every byte of data.

• #include <unistd.h> int execl(const char *path, const char *arg0, ... /*, (char *)0 */); int execv(const char *path, char *const argv[]); int execle(const char *path, const char *arg0, ... /*,        (char *)0, char *const envp[]*/); int execve(const char *path, char *const argv[], char *const envp[]); int execlp(const char *file, const char *arg0, ... /*, (char *)0 */); int execvp(const char *file, char *const argv[]); • The arguments specified by a program with one of the exec functions shall be passed on to the new process image in the corresponding main() arguments. • The argument path points to a pathname that identifies the new process image file.

• #include <sys/socket.h> • int getsockname(int socket, struct sockaddr *address, socklen_t *address_len); • The getsockname() function retrieves the locally-bound name of
the specified socket, stores this address in the sockaddr structure pointed to by the address argument, and stores the length of this address in the object pointed to by the address_len argument. If the actual length of the address is greater than the length of the supplied sockaddr structure, the stored address will be truncated. If the socket has not been bound to a local name, the value stored in the object pointed to by address is unspecified.

• •

• #include <sys/socket.h> • int getpeername(int socket, struct sockaddr *address, socklen_t *address_len);
• The getpeername() function retrieves the peer address of the specified socket, stores this address in the sockaddr structure pointed to by the address argument, and stores the length of this address in the object pointed to by the address_len argument. If the actual length of the address is greater than the length of the supplied sockaddr structure, the stored address will be truncated. If the protocol permits connections by unbound clients, and the peer is not bound, then the value stored in the object pointed to by address is unspecified.

• •

Socket Options

Managing socket options – 3 ways
• getsockopt() and setsockopt() • fcntl() • ioctl()

getsockopt() and setsockopt()
int getsockopt( int sockfd, int level, /* SOL_SOCKET, IPPROTO_IP, IPPROTO_TCP etc */ int optname, /* option name */ void *optval, /* option value */ socklen_t *optlen); /* data length of option*/

int setsockopt( int sockfd, int level, int optname, const void *optval, socklen_t optlen);

Two types of socket options
• Flag options
– Enable or disable a feature – Optval points to an integer which can be zero or non-zero

• Value options
– Pass a value to/from the OS – Optval points to some structure

Level of an Option
• Level of protocol hierarchy at which the option applies • • • • Socket layer level – SOL_SOCKET TCP Layer – IPPROTO_TCP IPv4 Layer – IPPROTO_IP IPv6 Layer – IPPROTO_IPV6

Some socket options
• • • SO_BROADCAST
– Enable socket for sending broadcast

SO_KEEPALIVE
– Regularly probes if connection is alive

SO_LINGER
– Linger till all data is sent before returning from close() on a TCP connection.

SO_RCVBUF/ SO_SNDBUF
– Size of receive/send buffers on socket.

SO_RCVTIMEO/SO_SNDTIMEO
– Place timeout on receive/send operation.

Some more socket options
• • • • • • IP_TOS
– Set type of service field in IP datagram

IP_TTL
– Set time-to-live field in IP datagram

IP_ADD_MEMBERSHIP / IP_DROP_MEMBERSHIP
– Join/Drop a multicast group

TCP_KEEPALIVE
– Probe timer for TCP connection probing

TCP_MAXSEG / TCP_MAXRT
– TCP maximum segment size, maximum retransmit time

More on Page 179 Steven’s book.

Non-Blocking I/O (polling)
Application recvfrom() Time System call
EWOULDBLOCK

Operating system No datagram ready

recvfrom()

System call
EWOULDBLOCK

No datagram ready

recvfrom()

System call

datagram ready Copy datagram

Process blocks

Copy data to user

Process datagram

Return ok

Copy complete

Signal driven I/O
Application
Establish SIGIO Signal handler

Operating system System call
return

No datagram ready Wait for data

Time

Process continues Signal Handler Deliver SIGIO

recvfrom()
Process blocks

System call

datagram ready Copy datagram
Copy data to user

Process datagram

Return ok

Copy complete

fcntl()
• int fcntl(int fd, int cmd, long arg); • Miscellaneous file control operations
– Non-blocking I/O (O_NONBLOCK, F_SETFL) – Signal-driven I/O (O_ASYNC, F_SETFL) – Set/Get socket owner (F_SETOWN, F_GETOWN)

Enabling Non-Blocking I/O
int flags; /* Set socket nonblocking */ if( (flags = fcntl(fd, F_GETFL, 0)) < 0){ perror(“fcntl”); exit(1); } flags |= O_NONBLOCK; if( fcntl(fd, F_SETFL, flags) < 0){ perror(“fcntl”); exit(1); }

ioctl()

ioctl()
• Handles miscellaneous properties of a file/device referenced by a descriptor.
– In our case, network interfaces.

• int ioctl(int fd, int request, void * arg) – Socket operations – File operations – Interface configuration – ARP cache – Routing table

– SIOSPGRP/SIOGPGRP – FIONREAD

ioctl()

• set/get process/group ID of a socket owner (same as fcntl)

• Return number of bytes in socket buffer

– SIOCGIFCONF
• Get list of all interfaces

– SIOCGIFBRDADDR/ SIOCSIFBRDADDR
• Get/set broadcast address

– SIOCGARP/SIOCSARP/SIOCDARP
• Get/modify/delete ARP cache entry.

– SIOCADDRT/SIOCDELRT
• Add/delete routes

Signal-driven I/O

Signal driven I/O
Application
Establish SIGIO Signal handler

Operating system System call
return

No datagram ready

Time

Process continues Deliver SIGIO Signal Handler

Wait for data

recvfrom()
Process blocks

System call

datagram ready Copy datagram Copy data to user

Process datagram

Return ok

Copy complete

Conditions that generate SIGIO
• UDP
– Datagram arrival – Socket errors

TCP (too many conditions! Mostly useless for TCP)
– – – – – – – Connection complete on listening socket Disconnect initiated Disconnect complete Half shutdown Data arrival No way to distinguish! Data sent (free o/p buffer) Socket error

Steps to Set Up Signal-Driven I/O
• • •

Establish signal handler for SIGIO Set socket owner using fcntl() with F_SETOWN Enable signal-driven I/O for socket
Using fcntl( ) or ioctl( )


Make socket Non-blocking
Don’t want to block inside signal handler.

Architecture for UDP
Main Server Loop

Datagram queue

SIGIO Handler User Space Kernel Data Arrival

sigio_handler
void sigio_handler(int arg) { int ret; while(1) { / * read data from socket */ ret = recvfrom(sockfd, …); if( ret < 0 ) { if( errno == EWOULDBLOCK ) break; /* done reading all packets */ else { perror(“recvfrom”); exit(0); /* read error */ } } Insert datagram in server queue… } }

Main Loop
sigset_t newmask, oldmask; int on; /* Step 1: set up handler for SIGIO */ Signal(SIGIO, sigio_handler); /* Step 2: Set socket owner */ Fcntl(sockfd, F_SETOWN, getpid()); /* Step 3: Set up socket for signal driven I/O */ on = 1 Ioctl(sockfd, FIOASYNC, &on); /* Alternately, use fcntl() */ /* Step 4: Make socket non-blocking! */ Ioctl(sockfd, FIONBIO, &on); /* Alternatively, use fcntl() */

Main Loop (contd.)
Sigemptyset(&oldmask); Sigemptyset(&newmask); Sigaddset(&newmask, SIGIO); while(1) { /* Block SIGIO temporarily */ Sigprocmask(SIG_BLOCK, &newmask, &oldmask); if(queue is not empty) Process datagrams in server queue… /* Unblock SIGIO */ Sigprocmask(SIG_SETMASK, &oldmask, NULL); Do something else… }

I/O Models I/O Multiplexing
Chapter 6 (UNP Book)

I/O Models
1. 2. 3. 4. 5. Blocking I/O Non-blocking I/O I/O multiplexing – select() Signal driven I/O Asynchronous I/O

Two steps in data reception
User Level System Call Interface 1.Data Arrival (local) Kernel Level Kernel Buffer Application Buffer 2. Copy

1. Data Arrival (network) Overheads: Context switching, Data copying

Blocking I/O
Application

recvfrom() Time
Process blocks

System call

Operating system

No datagram ready Wait for data datagram ready Copy datagram
Copy data to user

Read datagram

Return ok

Copy complete

Non-Blocking I/O (polling)
Application

recvfrom() Time recvfrom() recvfrom()
Process blocks

System call
EWOULDBLOCK

Operating system

No datagram ready No datagram ready datagram ready Copy datagram

System call
EWOULDBLOCK

System call

Copy data to user

Read datagram

Return ok

Copy complete

I/O Multiplexing
Concurrent Server
select()
listenfd

Sockets or fd1 Files descriptors Or Pipe descriptors Remote Clients Or files Or pipes

fd2

fd3

fd4

fd5

C1

C2

F3

F4

P5

I/O Multiplexing
Application
on multiple fds

Operating system

select()

System call

No datagram ready Wait for data on any fd datagram ready Copy datagram
Copy data to user

Time

Process blocks Return readable

recvfrom()
Process blocks

System call

Read datagram

Return ok

Copy complete

Signal driven I/O
Application Operating system

Establish SIGIO Signal handler

System call
return

No datagram ready Wait for data

Time
Process continues Signal Handler

Deliver SIGIO

recvfrom()
Process blocks

System call

datagram ready Copy datagram
Copy data to user

Read datagram

Return ok

Copy complete

Asynchronous I/O
Application

aio_read() Time
Process continues

System call return

Operating system

No datagram ready Wait for data datagram ready Copy datagram
Copy data to user

Signal handler Read datagram

Deliver signal specified in aio_read()

Copy complete

I/O Multiplexing

What is I/O multiplexing?
• When an application needs to handle multiple I/O descriptors at the same time
– E.g. file and socket descriptors, multiple socket descriptors

• When I/O on any one descriptor can result in blocking

Non-forking concurrent server
Concurrent Server
select()
listenfd

Sockets 

fd1

fd2

fd3

fd4

fd5

Clients  C1

C2

C3

C4

C5

select() call
• Allows a process to wait for an event to occur on any one of its descriptors.

• Types of event
– ready for read – ready for write – Exception condition

select() call
int select(

int maxfdp1, /* max. fd + 1 */ fd_set *readfds, /* read ready? */ fd_set *writefds, /* write ready? */ fd_set *exceptfds, /* exceptions? */ struct timeval *timeout);
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }

struct fd_set
• Set of descriptors that we want to wait on for events. • Typically holds 256 descriptor states. • Manipulation macros
– – – – void FD_ZERO(fd_set *fds) void FD_SET (int fd, fd_set *fds) void FD_CLR (int fd, fd_set *fds) int FD_ISSET(int fd, fd_set *fds)

Non-forking Concurrent Server
fdset rdset, wrset; int listenfd, connfd1, connfd2; int maxfdp1; ……… Connection establishment etc. ……… /* initialize */ FD_ZERO(&rdset); FD_ZERO(&wrset);

for( ;; ) { FD_SET(connfd1, &rdset); FD_SET(connfd2, &wrset); FD_SET(listenfd, &rdset); maxfdp1 = max(connfd1, connfd2, listenfd) + 1; /* wait for some event */ Select(maxfdp1, &rdset, &wrset, NULL, NULL); if( FD_ISSET(connfd1, &rdset) ) {
Read data from connfd1…

} if( FD_ISSET(connfd2, &wrset) ) {
Write data to connfd2…

} if( FD_ISSET(listenfd, &rdset) {
Process a new connection…

}

Pselect()
• Int pselect(int maxfdp1,fd_set *readset,fd_set *writeset, fd_set *exceptset, const struct timespec *timeout, const sigset_t *sigmask); • Reads count of ready descriptors,o on timeout, -1 on error • Struct timespec{ Time _t tv_sec; Long tv_nsec; }; Sigmask enables the program to disable the delivery of certain signals, test global variables for dis abled signals, then calls pselect to reset the signal mask.

• Test for events on multiple sockets simultaneously • Prototypes • #include <sys/poll.h> int poll(struct pollfd *ufds, unsigned int nfds, int timeout); • Description • The basic idea is that you pass an array of nfds struct pollfds in ufds, along with a timeout in milliseconds • Each element in the array of struct pollfds represents one socket descriptor, and contains the following fields:

poll()

• • • • • • • • • • • • • •

struct pollfd { int fd; // the socket descriptor short events; // bitmap of events we’re interested in short revents; // when poll() returns, bitmap of events that occurred }; Before calling poll(), load fd with the socket descriptor POLLIN Alert me when data is ready to recv() on this socket. POLLOUT Alert me when I can send() data to this socket without blocking. POLLPRI Alert me when outofband data is ready to recv() on this socket. POLLERR An error has occurred on this socket. POLLHUP The remote side of the connection hung up. POLLNVAL Something was wrong with the socket descriptor fd– maybe it’s Return Value Returns the number of elements in the ufds array that have had event occur on them; this can be zero if the timeout occurred. Also returns -1 on error (and errno will be set accordingly.)

• Close – Decrements descriptors reference count and closes socket when count reaches 0 • Close terminates both direction of data transfer • Shutdown does half close- other end can still send data after one end of the connection closes.

Shutdown()

• Int Shutdown(int sockfd, int howto)
• • • • SHUT_RD : READ HALF OF CONNECTION IS CLOSED SHUT_WR : WRITE HALF OF CONNECTION IS CLOSED READ AND WRITE HALF OF CONNECTION CLOSED SHUT_RDWR

poll()
Test for events on multiple sockets simultaneously Prototypes #include <sys/poll.h> int poll(struct pollfd *ufds, unsigned int nfds, int timeout); Description This function is very similar to select() in that they both watch sets of file descriptors forevents, such as incoming data ready to recv(), socket ready to send() data to, outofband data ready to recv(), errors, etc.The basic idea is that you pass an array of nfds struct pollfds in ufds, along with a timeout in milliseconds (1000 milliseconds in a second.) The timeout can be negative if you want to wait forever. If no event happen on any of the socket descriptors by the timeout, poll() will return.Each element in the array of struct pollfds represents one socket descriptor, and contains the following fields: struct pollfd { int fd; // the socket descriptor short events; // bitmap of events we’re interested in short revents; // when poll() returns, bitmap of events that occurred }; Before calling poll(), load fd with the socket descriptor POLLIN Alert me when data is ready to recv() on this socket. POLLOUT Alert me when I can send() data to this socket without blocking. POLLPRI Alert me when outofband data is ready to recv() on this socket. POLLERR An error has occurred on this socket. POLLHUP The remote side of the connection hung up. POLLNVAL Something was wrong with the socket descriptor fd–maybe it’s uninitialized? Return Value Returns the number of elements in the ufds array that have had event occur on them; this can be zero if the timeout occurred. Also returns -1 on error (and errno will be set accordingly.)

Sign up to vote on this title
UsefulNot useful