![]()
Socket Patterns
CptS/EE 455 - Computer Communication Networks - Fall 2016 Washington State University |
|
Socket PatternsThere are a few essential patterns that you need to learn for effective socket programming. The most basic ones contained in the lecture from last time. Let's reconstruct them: (Book references: chapters 2 and 4)Stream communicationsServers1 = socket(...); # args: protocol family, datagram or stream, protocol bind(s1, ...); # passing address family and address listen(s1, backlog); while (still serving) { s2 = accept(s1); while (still talking to current client) { send(s2, ...) and recv(s2, ...) } close(s2); } close(s1); Clients1 = socket(...); connect(s1, ...); # passing address family and address while (still talking to server) { send(s1, ...) and recv(s1, ...); } close(s1);Remember that a stream socket does not preserve message boundaries -- nth recv may read more or fewer bytes than the nth send. However, bytes will always be received in the order that they were sent. Also, a send() operation may not accept all of the bytes that were specified to be sent. Why? Consequences of these two things for programming? Patterns for datagramsServers1 = socket(...); while (still serving) { sendto(s1, address(in), ...) and recvfrom(s1, address (out), ...) to talk to any and all clients } close(s1); ClientJust like the server! But notice about the requirements on the order of sendto and recvfrom in the different programs! Datagram communication has the advantage that message boundaries are preserved, but messages may be lost, re-ordered, or duplicated.Limitations of the basic patterns
Forking server patternWe can overcome the first problem by using a separate process (or thread) to handle each client. Once a connection is accept'ed responsibility for reading and writing the new socket descriptor is handed off to a new (or pre-existing) process (or thread).Note the additional closes. Remember that file and socket descriptors are copied into the new process. The child does not need a descriptor for the listening socket and the parent does not need a descriptor for client interaction socket. int childCount = 0; s1 = socket(...); # args: protocol family, datagram or stream, protocol bind(s1, ...); # passing address family and address listen(s1, backlog); while (still serving) { s2 = accept(s1); if (fork()==0) { close(s1); # closing fd does not close socket unless last reference while (still talking to current client) { send(s2, ...) and recv(s2, ...) } close(s2); } close(s2); childCount++; # reap children here! while (childCount) { pid = waitpid(-1, NULL. WNOHANG); if (pid==0) break; else childCount--; } } close(s1); Using selectThe select system call allows a process to figure out which send and receive operations it can perform without waiting, and to wait for one of many fds to become ready for reading, writing, or error handling. The format isselect(int maxDescPlus1, fd_set *readDescs, fd_set *writeDescs, fd_set *exceptionDescs, struct timeval *timeout)Select suspends the process until either one of the specified fds becomes ready or the timeout occurs. To wait forever pass NULL as the timeout. To not wait, pass a timeval representing 0 time. The fd_set type represents a bit vector in which a bit is '1' iff the corresponding fd is in the set. fd_sets are manipulated with macros FD_ZERO(fd_set *set) FD_CLR(int fd, fd_set *set) FD_SET(int fd, fd_set *set) FD_ISSET(int fd, fd_set *set)Note that the first parameter of select needs to be
Select returns the number of descriptors ready to perform I/O and You can use select with all empty fd sets to do a precision sleep on systems that only have the sleep system call which only allows sleeping for integral numbers of seconds. |
|