480 likes | 566 Views
Lecture 11 Overview. Creating a TCP socket. int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); int mysock; struct sockaddr_in myaddr; mysock = socket(PF_INET,SOCK_STREAM,0); myaddr.sin_family = AF_INET; myaddr.sin_port = htons( 80 );
E N D
Creating a TCP socket int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); int mysock; struct sockaddr_in myaddr; mysock = socket(PF_INET,SOCK_STREAM,0); myaddr.sin_family = AF_INET; myaddr.sin_port = htons( 80 ); myaddr.sin_addr = htonl( INADDR_ANY ); bind(mysock, (sockaddr *) &myaddr, sizeof(myaddr)); CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Establishing a passive mode TCP socket • Passive mode: • Address already determined • Tell the kernel to accept incoming connection requests directed at the socket address • 3-way handshake • Tell the kernel to queue incoming connections for us CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
listen() int listen(int sockfd, int backlog); • sockfd is the TCP socket • already bound to an address • backlog is the number of incoming connections the kernel should be able to keep track of (queue for us) • Sum of incomplete and completed queues CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Accepting an incoming connection • Once we call listen(), the O.S. will queue incoming connections • Handles the 3-way handshake • Queues up multiple connections • When our application is ready to handle a new connection • we need to ask the O.S. for the next connection CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
accept() int accept( int sockfd, struct sockaddr* cliaddr, socklen_t *addrlen); • sockfd is the passive mode TCP socket • initiated by socket(), bind(), and listen() • cliaddr is a pointer to allocated space • addrlen is a value-result argument • must be set to the size of cliaddr • on return, will be set to be the number of used bytes in cliaddr CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Terminating a TCP connection int close(int sockfd); • Either end of the connection can call the close() system call • What if there is data being sent? • If the other end has closed the connection, and there is no buffered data, reading from a TCP socket returns 0 to indicate EOF. CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Client Code • TCP clients can call connect() which: • takes care of establishing an endpoint address for the client socket • Attempts to establish a connection to the specified server • 3-way handshake • no need to call bind first, the O.S. will take care of assigning the local endpoint address • TCP port number, IP address CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
connect() int connect( int sockfd, const struct sockaddr *server, socklen_t addrlen); • sockfd is an already created TCP socket • server contains the address of the server • connect() returns 0 if OK, -1 on error • No response to SYN segment (3 trials) • RST signal • ICMP destination unreachable (3 trials) CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Reading from a TCP socket int read(int fd, char *buf, int max); • By default read() will block until data is available • reading from a TCP socket may return less than max bytes • whatever is available • You must be prepared to read data 1 byte at a time! CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Writing to a TCP socket int write(int fd, char *buf, int num); • write might not be able to write all num bytes on a nonblocking socket • readn(), writen() and readline() functions CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Creating a UDP socket int mysock; struct sockaddr_in myaddr; Mysock=socket(PF_INET,SOCK_DGRAM,0); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(1234); myaddr.sin_addr = htonl(INADDR_ANY); bind(mysock, &myaddr, sizeof(myaddr)); CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Sending UDP Datagrams ssize_t sendto( int sockfd, void *buff, size_t nbytes, int flags, const struct sockaddr* to, socklen_t addrlen); • sockfd is a UDP socket • buff is the address of the data (nbytes long) • to is the destination address • Return value is the number of bytes sent, • or -1 on error. CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
sendto() • The return value of sendto() indicates how much data was accepted by the O.S. for sending as a datagram • not how much data made it to the destination. • There is no error condition that indicates that the destination did not get the data!!! • You can send 0 bytes of data! CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Receiving UDP Datagrams ssize_t recvfrom( int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr* from, socklen_t *fromaddrlen); • sockfd is a UDP socket • buff is the address of a buffer (nbytes long) • from is the address of a sockaddr • Return value is the number of bytes received and put into buff, or -1 on error CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
recvfrom() • If buff is not large enough, any extra data is lost forever... • You can receive 0 bytes of data! • recvfrom doesn’t return until there is a datagram available, • You should set fromaddrlen before calling • If from and fromaddrlen are NULL we don’t find out who sent the data CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Typical UDP Communication CPE 401/601 Lecture 11 : TCP & UDP Socket Programming
Lecture 12Socket Programming Issues CPE 401 / 601 Computer Network Systems slides are modified from Dave Hollinger slides are modified from Dave Hollinger
Debugging • Debugging can be difficult • Write routines to print out sockaddrs • Use trace, strace, ptrace, truss, etc • Include code that can handle unexpected situations CPE 401/601 Lecture 12 : Socket Programming Issues
Timeout when calling recvfrom() • It might be nice to have each call to recvfrom() return after a specified period of time even if there is no incoming datagram • We can do this by using SIGALRM and wrapping each call to recvfrom() with a call to alarm() There are some other (better) ways to do this CPE 401/601 Lecture 12 : Socket Programming Issues
UDP Connected mode • A UDP socket can be used in a call to connect() • This simply tells the O.S. the address of the peer • No handshake is made to establish that the peer exists • No data of any kind is sent on the network as a result of calling connect() on a UDP socket CPE 401/601 Lecture 12 : Socket Programming Issues
Connected UDP • Once a UDP socket is connected: • can use sendto() with a null dest address • can use write() and send() • can use read() and recv() • only datagrams from the peer will be returned • Asynchronous errors will be returned to the process OS Specific, some won’t do this! CPE 401/601 Lecture 12 : Socket Programming Issues
Asynchronous Errors • What happens if a client sends data to a server that is not running? • ICMP “port unreachable” error is generated by receiving host and sent to sending host • The ICMP error may reach the sending host after sendto() has already returned! • The next call dealing with the socket could return the error CPE 401/601 Lecture 12 : Socket Programming Issues
Back to UDP connect() • Connect() is typically used with UDP when communication is with a single peer only • It is possible to disconnect and connect the same socket to a new peer • More efficient to send multiple datagrams to the same user • Many UDP clients use connect() • Some servers (TFTP) CPE 401/601 Lecture 12 : Socket Programming Issues
I/O Multiplexing • We often need to be able to monitor multiple descriptors: • a generic TCP client (like telnet) • a server that handles both TCP and UDP • Client that can make multiple concurrent requests • browser CPE 401/601 Lecture 12 : I/O Multiplexing
Example - generic TCP client • Input from standard input should be sent to a TCP socket • Input from a TCP socket should be sent to standard output • How do we know when to check for input from each source? STDIN TCP SOCKET STDOUT CPE 401/601 Lecture 12 : I/O Multiplexing
Options • Use multiple processes/threads • Use nonblocking I/O • use fcntl() to set O_NONBLOCK • Use alarm and signal handler to interrupt slow system calls • Use functions that support checking of multiple input sources at the same time CPE 401/601 Lecture 12 : I/O Multiplexing
Non blocking I/O • Tell kernel not to block a process if I/O requests can not be completed • use fcntl() to set O_NONBLOCK: int flags; flags = fcntl(sock,F_GETFL,0); fcntl(sock,F_SETFL,flags | O_NONBLOCK); • Now calls to read() (and other system calls) will return an error and set errno to EWOULDBLOCK CPE 401/601 Lecture 12 : I/O Multiplexing
Non blocking I/O while (! done) { if ( (n=read(STDIN_FILENO,…)<0)) if (errno != EWOULDBLOCK) /* ERROR */ else write(tcpsock,…) if ( (n=read(tcpsock,…)<0)) if (errno != EWOULDBLOCK) /* ERROR */ else write(STDOUT_FILENO,…) } CPE 401/601 Lecture 12 : I/O Multiplexing
The problem with nonblocking I/O • Using blocking I/O allows the OS to put your process to sleep when nothing is happening • Once input arrives, the OS will wake up your process and read() (or whatever) will return • With nonblocking I/O, the process will chew up all available processor time!!! CPE 401/601 Lecture 12 : I/O Multiplexing
Using alarms signal(SIGALRM, sig_alrm); alarm(MAX_TIME); read(STDIN_FILENO,…); ... signal(SIGALRM, sig_alrm); alarm(MAX_TIME); read(tcpsock,…); ... A function you write CPE 401/601 Lecture 12 : I/O Multiplexing
“Alarming” Issues • What will happen to the response time ? • What is the ‘right’ value for MAX_TIME? CPE 401/601 Lecture 12 : I/O Multiplexing
Select() • The select() system call allows us to use blocking I/O on a set of descriptors • file, socket, … • We can ask select to notify us when data is available for reading on either STDIN or a socket CPE 401/601 Lecture 12 : I/O Multiplexing
select() int select( int maxfd, fd_set *readset, fd_set *writeset, fd_set *excepset, const struct timeval *timeout); • maxfd: highest number assigned to a descriptor • readset: set of descriptors we want to read from • writeset: set of descriptors we want to write to • excepset: set of descriptors to watch for exceptions • timeout: maximum time select should wait CPE 401/601 Lecture 12 : I/O Multiplexing
struct timeval struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ } struct timeval max = {1,0}; • To return immediately after checking descriptors • set timeout as {0, 0} • To wait until I/O is ready • set timeout as a NULL pointer CPE 401/601 Lecture 12 : I/O Multiplexing
fd_set • Operations you can use with an fd_set: • Clear all bits in fd_set void FD_ZERO(fd_set *fdset); • Turn on the bit for fd in fd_set void FD_SET(int fd, fd_set *fdset); • Turn off the bit for fd in fd_set void FD_CLR(int fd, fd_set *fdset); • Check whether the bit for fd in fd_set is on int FD_ISSET(int fd, fd_set *fdset); CPE 401/601 Lecture 12 : I/O Multiplexing
Using select() • Create fd_set • Clear the whole thing with FD_ZERO • Add each descriptor you want to watch using FD_SET • Call select • when select returns, use FD_ISSET to see if I/O is possible on each descriptor CPE 401/601 Lecture 12 : I/O Multiplexing
Issues and Ideas Error Handling
System Calls and Errors • In general, systems calls return a negative number to indicate an error • We often want to find out what error • Servers generally add this information to a log • Clients generally provide some information to the user CPE 401/601 Lecture 12 : Error Handling
extern int errno; • Whenever an error occurs, system calls set the value of the global variable errno • You can check errno for specific errors • errno is valid only after a system call has returned an error • System calls don't clear errno on success • If you make another system call you may lose the previous value of errno • printf makes a call to write! CPE 401/601 Lecture 12 : Error Handling
Error codes • Error codes are defined in errno.h EAGAIN EBADF EACCESS EBUSY EINTR EINVAL EIO ENODEV EPIPE … • Support routines • void perror(const char *string); • stdio.h • char *strerror(int errnum); • string.h CPE 401/601 Lecture 12 : Error Handling
General Strategies • Include code to check for errors after every system call • Develop "wrapper functions" that do the checking for you • Develop layers of functions, each hides some of the error-handling details CPE 401/601 Lecture 12 : Error Handling
Example wrapper int Socket( int f, int t, int p) { int n; if ( (n=socket(f,t,p)) < 0 ) ) { perror("Fatal Error"); exit(1); } return(n); } CPE 401/601 Lecture 12 : Error Handling
What is fatal? • How do you know what should be a fatal error? • Common sense • If the program can continue – it should • if a server can't create a socket, or can't bind to it's port • there is no sense continuing… CPE 401/601 Lecture 12 : Error Handling
Wrappers are great! • Wrappers like those used in the text can make code much more readable • There are always situations in which you cannot use the wrappers • Sometimes system calls are "interrupted" (EINTR) • this is not always a fatal error ! CPE 401/601 Lecture 12 : Error Handling
Another approach • Instead of simple wrapper functions, you might develop a layered system • The idea is to "hide" the sockaddr and error handling details behind a few custom functions: • int tcp_client(char *server, int port); • int tcp_server(int port); CPE 401/601 Lecture 12 : Error Handling
Layers and Code Re-use • Developing general functions that might be re-used in other programs is obviously "a good thing" • Layering is beneficial even if the code is not intended to be re-used: • hide error-handling from "high-level" code • hide other details • often makes debugging easier CPE 401/601 Lecture 12 : Error Handling
The Best Approach to handling errors • There is no best approach • Do what works for you • Make sure you check all system calls for errors! • Not checking can lead to security problems! • Not checking can lead to bad grades on assignments! CPE 401/601 Lecture 12 : Error Handling