580 likes | 601 Views
TDC561 Network Programming. Week 5: Client/Server Programming Aspects Client side: Identifying the Server; Specifying a local IP address; TCP client design, UDP client design. Server Side: Daemon, inetd.
E N D
TDC561 Network Programming Week 5: Client/Server Programming Aspects • Client side: Identifying the Server; Specifying a local IP address; TCP client design, UDP client design. • Server Side: Daemon, inetd. Socket Options ( discussion will continue in a next lecture ) Camelia Zlatea, PhD Email: czlatea@cs.depaul.edu
Douglas Comer, David Stevens, Internetworking with TCP/IP : Client-Server Programming, Volume III (BSD Unix and ANSI C), 2nd edition, 1996 (ISBN 0-13-260969-X) Chap. 6, 14 (partial) W. Richard Stevens, Network Programming : Networking API: Sockets and XTI, Volume 1, 2nd edition, 1998 (ISBN 0-13-490012-X) Chap. 7,11,12, 21,22 References
Aspects of Client/Server Programming • Client-side aspects: • Identifying the Server. • Looking up an IP address. • Looking up a well known port name. • Specifying a local IP address. • TCP client design. • UDP client design. • Server-side aspects: • daemons • inetd
Identifying the Server • Need an IP address, protocol and port. • often use host names instead of IP addresses. • usually the protocol (UDP vs. TCP) is not specified by the user. • often the port is not specified by the user. • Options: • hard-coded into the client program. • require that the user identify the server. • read from a configuration file. • use a separate network service to lookup the identity of the server.
Services and Ports • Identifying of the server • Many services are available via “well known” addresses (names). • There is a mapping of service names to port numbers: • struct *servent getservbyname( char *service, char *protocol ); • servent->s_port is the port number in network byte order. • Specifying a Local Address • When a client creates and binds a socket it must specify a local port and an IP address. • A client ask the kernel to assign the local IP address: haddr->sin_addr.s_addr= htonl(INADDR_ANY); • Typically a randomly port available is assigned with: haddr->port = htons(0);
Example: Identifying the Server • Option: read from a configuration file. /* Initialize the server structure */ server.sin_addr.s_addr = INADDR_ANY; server.sin_family = AF_INET; /* Get the service port associated with this process usually in some /etc/ config file such as /etc/services */ servent_p = getservbyname ( PMSERVICE, “tcp” ); server.sin_port = servent_p->s_port;
DNS library functions: gethostbyname #include <netdb.h> struct hostent *gethostbyname( const char *hostname); struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; };
hostent Official Name h_name h_aliases h_addrtype h_length h_addr_list alias 1 alias 2 null h_addr_list[0] (IP address 1) h_addr_list[1] (IP address 2) null All the IP addresses returned via the hostent are in network byte order #define h_addr h_addr_list[0]
DNS library functions: gethostbyname • On error gethostbyname return null. • gethostbyname sets the global variable h_errno to indicate the exact error: • HOST_NOT_FOUND • TRY_AGAIN • NO_RECOVERY • NO_DATA • NO_ADDRESS
Getting host IP address: char **h_addr_list; h = gethostbyname(“hawk.depaul.edu"); memcpy(&sockaddr.sin_addr, h->h_addr_list[0], sizeof(struct in_addr));
DNS library functions: gethostbyaddr struct hostent *gethostbyaddr( const char *addr size_t len, int family);
Other DNS library functions • Shell Command uname –n • get hostname of local host • uname –aprints all info local host • getservbyname • get port number for a named service • getservbyaddr • get name for service associated with a port number
Name/Address Conversion • Protocol dependent DNS library functions • gethostbyname • gethostbyaddr • Posix protocol independent functions • getaddrinfo() • getnameinfo() • getaddrinfo()and getnameinfo() functions provide name/address conversions as part of the sockets library. • designed to support writing code that can run on many protocols (IPv4, IPv6)
POSIX: getaddrinfo() int getaddrinfo( const char *hostname, const char *service, const struct addrinfo* hints, struct addrinfo **result); • hostname is a hostname or an address string (dotted decimal string for IP). • service is a service name or a decimal port number string. • getaddrinfo() • provides the combined functionality of gethostbyname()andgetservbyname()
POSIX: getaddrinfo() • result is returned with the address of a pointer to an addrinfo structure that is the head of a linked list. struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char *canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; }; used by socket() used by: bind() connect() sendto()
POSIX: getnameinfo() int getnameinfo( const struct sockaddr *sockaddr, socklen_t addrlen char *host, size_t hostlen, char *serv, size_t servlen, int flags); getnameinfo() looks up a hostname and a service name given a sockaddr
TCP Client Design • Identify and establish server address (IP and port). • Allocate a socket. • Request OS to use any valid local port and IP address • Call connect() • Communicate with server (read,write). • Close the connection.
Stream Socket Transaction (TCP Connection) Server socket() Client bind() socket() listen() 3-way handshake connect() accept() write() data read() data write() read() EOF close() read() close()
Closing a TCP socket • Many TCP based application protocols support multiple requests and/or variable length requests over a single TCP connection. • How does the server known when the client is done ? • When it is safe to close the socket?
Partial Close • One solution is for the client to shut down only it’s writing end of the socket. • The shutdown() system call provides this function. shutdown( int s, int direction); • direction can be 0 to close the reading end or 1 to close the writing end. • shutdown sends info to the other process
TCP Sockets Programming • Typical issues : • reads don’t correspond to writes. • synchronization (including close()) • TCP Reads: • Each call to read() on a TCP socket returns any available data (up to a maximum). • TCP buffers data at both ends of the connection.
UDP Client Design • Identify and establish the server address (IP and port). • Allocate a socket. • Request OS to use any valid local port and IP address • Communicate with server (send, recv) • Close the socket.
Datagram Socket Transaction (UDP Connection) Server Client socket() socket() bind() sendto() data recvfrom() data sendto() recvfrom() close()
Datagram Sockets • Connectionless sockets, i.e., C/S addresses are passed along with each message sent from one process to another • Peer-to-Peer Communication • Provides an interface to the UDP datagram services • Handles network transmission as independent packets • Provides no guarantees, although it does include a checksum • Does not detect duplicates • Does not determine sequence • ie information can be lost, wrong order or duplicated
Daemons • Most servers run as a daemon process • Typical UNIX daemons: • Web server (httpd) • Mail server (sendmail) • SuperServer (inetd) • System logging (syslogd) • Print server (lpd) • router process (routed, gated)
Daemon Process • A daemon is a process that: • runs in the background • not associated with any terminal • output doesn't end up in another session. • ignore SIGINT (such as terminal generated signals - ^C • since is not associated with any terminal uses • file system • central logging facility • daemons should close all unnecessary descriptors • often including stdin, stdout, stderr. • daemons often change working directory. • A background process • Parent process fork() and then the parent exit(). • A way to disassociate a process from a terminal. • Call setsid() and then fork() again.
Daemon Process Init – Example #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int daemon_init(void) { pid_t pid; if ( (pid = fork()) < 0) return(-1); else if (pid != 0) exit(0); /* parent is gone */ /* child continues */ setsid(); /* become session leader */ system(“cd /"); /* change working directory */ umask(0); /* clear our file mode creation mask */ close(0); close(1); close(2); /* stdin, stdout, stderr */ return(0); }
Performance issues with daemons processes • CPU utilization • There can be many servers running as daemons -and idle most of the time. • Memory utilization • Most daemons/servers are suspended most of the time, but still occupy space in the process table. • A solution: Superserver or inetd (Internet services daemon )
inetd • the SuperServer is named inetd. • inetd daemon creates multiple sockets and waits for (multiple) incoming requests. • inetd typically uses select to watch multiple sockets for input. • when a request arrives, inetd will fork and the child process handles the client. • inetd child process closes all unnecessary sockets. • inetd child dup’s the client socket to descriptors 0,1 and 2 (stdin, stdout, stderr). • inetd child exec’s the real server program, which handles the request and exits.
inetd based servers • Servers that are started by inetd assume that the socket holding the request is already established (descriptors 0,1 or 2). • TCP servers started by inetd don’t call accept, so they must call getpeername if they need to know the address of the client. • inetd reads a configuration file that lists all the services it should handle. • /etc/inetd.conf • inetd creates a socket for each listed service, and adds the socket to a fd_set given to select().
getpeername #include <sys/types.h> #include <sys/socket.h> int getpeername(int s, struct sockaddr *name, socklen_t *namelen); • getpeername() returns the name of the peer connected to socket s.
inetd service specification • For each service, inetd needs to know: • the socket type and transport protocol • wait/nowait flag. • login name the process should run as. • pathname of real server program. • command line arguments to server program. • Servers that are expected to deal with frequent requests are typically not run from inetd • mail, web, NFS.
Example /etc/inetd.conf # Syntax for socket-based Internet services: # <service_name> <socket_type> <proto> <flags> <user> <server_pathname> <args> # # comments start with # echo stream tcp nowait root internal echo dgram udp wait root internal chargen stream tcp nowait root internal chargen dgram udp wait root internal ftp stream tcp nowait root /usr/sbin/ftpd ftpd -l telnet stream tcp nowait root /usr/sbin/telnetd telnetd finger stream tcp nowait root /usr/sbin/fingerd fingerd # Authentication auth stream tcp nowait nobody /usr/sbin/in.identd in.identd -l -e -o # TFTP tftp dgram udp wait root /usr/sbin/tftpd tftpd -s /tftpboot
wait/nowait • WAIT specifies that inetd should not look for new clients for the service until the child (the real server) has terminated. • TCP servers usually specify nowait - this means inetd can start multiple copies of the TCP server program - providing concurrency • Most UDP services run with inetd told to wait until the child server has died.
Topic Socket Options(discussion will continue in a next lecture)
Socket Options • Various attributes that are used to determine the behavior of sockets. • 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). • Other options are associated with more complex types including int, timeval, in_addr, sockaddr, etc.
Read-Only Socket Options • Some options are readable only (we can’t set the value).
Setting and Getting option values #include <sys/socket.h> getsockopt() gets the current value of a socket option. setsockopt() is used to set the value of a socket option.
getsockopt() int getsockopt( int sockfd, int level, int optname, void *opval, socklen_t *optlen); level specifies whether the option is a general option or a protocol specific option (what level of code should interpret the option).
setsockopt() int setsockopt( int sockfd, int level, int optname, const void *opval, socklen_t optlen);
General Options • Protocol independent options. • Handled by the generic socket system code. • Some general options are supported only by specific types of sockets (SOCK_DGRAM, SOCK_STREAM).
Some Generic Options SO_BROADCAST SO_DONTROUTE SO_ERROR SO_KEEPALIVE SO_LINGER SO_RCVBUF,SO_SNDBUF SO_REUSEADDR
SO_BROADCAST • Boolean option: enables/disables sending of broadcast messages. • Underlying data layer must support broadcasting • Applies only to SOCK_DGRAM sockets. • Prevents applications from inadvertently sending broadcasts (OS looks for this flag when broadcast address is specified).
SO_DONTROUTE • Boolean option: enables bypassing of normal routing. • Used by routing daemons.
SO_ERROR • Integer value option. • The value is an error indicator value (similar to errno). • Readable only • Reading (by calling getsockopt()) clears any pending error.
SO_KEEPALIVE • 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.
SO_LINGER Value is of type: struct linger { int l_onoff; /* 0 = off */ int l_linger; /* time in seconds */ }; • Used to control whether and how long a call to close will wait for pending ACKS. • connection-oriented sockets only.
SO_LINGER usage • By default, calling close() on a TCP socket will return immediately. • The closing process has no way of knowing whether or not the peer received all data. • Setting SO_LINGER means the closing process can determine that the peer machine has received the data (but not that the data has been read() !).
shutdown() vs SO_LINGER • How you can use shutdown() to find out when the peer process has read all the sent data [see R.Stevens, 7.5]