Live555中的Select

       Live555的关键函数是void BasicTaskScheduler::SingleStep(unsigned maxDelayTime),就是这个函数不停的循环执行才驱动了整个Live555的运行。因此,阅读和分析Live555的原理,关键之一就是要搞懂这个函数。 SingleStep中使用了select这种网络编程I/O模式来实现对多个网络套接字的管理。因此,我们有必要搞清楚win32下的select编程的相关知识。

       Windows下的网络编程,首推的参考书籍当之无愧的肯定是《Network Programming for Microsoft Windows》第二版,下面是书中对select的介绍:

       

The select Model

The select model is another I/O model widely available in Winsock. We call it the select model because it centers on using the select function to manage I/O. The design of this model originated on UNIX-based computers featuring Berkeley socket implementations. The select model was incorporated into Winsock 1.1 to allow applications that want to avoid blocking on socket calls the capability to manage multiple sockets in an organized manner. Because Winsock 1.1 is backward-compatible with Berkeley socket implementations, a Berkeley socket application that uses the select function should technically be able to run without modification.

The select function can be used to determine if there is data on a socket and if a socket can be written to. The reason for having this function is to prevent your application from blocking on an I/O bound call such as send or recv when a socket is in a blocking mode and to prevent the WSAEWOULDBLOCK error when a socket is in a non-blocking mode. The select function blocks for I/O operations until the conditions specified as parameters are met. The function prototype for select is as follows:

int select(
    int nfds,
    fd_set FAR * readfds,
    fd_set FAR * writefds,
    fd_set FAR * exceptfds,
    const struct timeval FAR * timeout
);

The first parameter, nfds, is ignored and is included only for compatibility with Berkeley socket applications. You'll notice that there are three fd_set parameters: one for checking readability (readfds), one for writeability (writefds), and one for out-of-band data (exceptfds). Essentially, the fd_set data type represents a collection of sockets. The readfds set identifies sockets that meet one of the following conditions:

  • Data is available for reading.

  • Connection has been closed, reset, or terminated.

  • If listen has been called and a connection is pending, the accept function will succeed.

The writefds set identifies sockets in which one of the following is true:

  • Data can be sent.

  • If a non-blocking connect call is being processed, the connection has succeeded.

Finally, the exceptfds set identifies sockets in which one of the following is true:

  • If a non-blocking connect call is being processed, the connection attempt failed.

  • OOB data is available for reading.

For example, when you want to test a socket for readability, you must add it to the readfds set and wait for the select function to complete. When the select call completes, you have to determine if your socket is still part of the readfds set. If so, the socket is readable—you can begin to retrieve data from it. Any two of the three parameters (readfds, writefds, exceptfds) can be null values (at least one must not be null), and any non-null set must contain at least one socket handle; otherwise, the select function won't have anything to wait for. The final parameter, timeout, is a pointer to a timeval structure that determines how long the select function will wait for I/O to complete. If timeout is a null pointer, select will block indefinitely until at least one descriptor meets the specified criteria. The timeval structure is defined as

struct timeval 
{
    long tv_sec; 
    long tv_usec;
};

The tv_sec field indicates how long to wait in seconds; the tv_usec field indicates how long to wait in milliseconds. The timeout value {0, 0} indicates select will return immediately, allowing an application to poll on the select operation. This should be avoided for performance reasons. When select completes successfully, it returns the total number of socket handles that have I/O operations pending in the fd_set structures. If the timeval limit expires, it returns 0. If select fails for any reason, it returns SOCKET_ERROR.

Before you can begin to use select to monitor sockets, your application has to set up either one or all of the read, write, and exception fd_set structures by assigning socket handles to a set. When you assign a socket to one of the sets, you are asking select to let you know if the I/O activities just described have occurred on a socket. Winsock provides the following set of macros to manipulate and check the fd_set sets for I/O activity.

  • FD_ZERO(*set) Initializes set to the empty set. A set should always be cleared before using.

  • FD_CLR(s, *set) Removes socket s from set.

  • FD_ISSET(s, *set) Checks to see if s is a member of set and returns TRUE if so.

  • FD_SET(s, *set) Adds socket s to set.

For example, if you want to find out when it is safe to read data from a socket without blocking, simply assign your socket to the fd_read set using the FD_SET macro and then call select. To test whether your socket is still part of the fd_read set, use the FD_ISSET macro. The following five steps describe the basic flow of an application that uses select with one or more socket handles:

  1. Initialize each fd_set of interest by using the FD_ZERO macro.

  2. Assign socket handles to each of the fd_set sets of interest by using the FD_SET macro.

  3. Call the select function and wait until I/O activity sets one or more of the socket handles in each fd_set set provided. When select completes, it returns the total number of socket handles that are set in all of the fd_set sets and updates each set accordingly.

  4. Using the return value of select, your application can determine which application sockets have I/O pending by checking each fd_set set using the FD_ISSET macro.

  5. After determining which sockets have I/O pending in each of the sets, process the I/O and go to step 1 to continue the select process.

When select returns, it modifies each of the fd_set structures by removing the socket handles that do not have pending I/O operations. This is why you should use the FD_ISSET macro as in step 4 to determine if a particular socket is part of a set. The following code sample outlines the basic steps needed to set up the select model for a single socket. Adding more sockets to this application simply involves maintaining a list or an array of additional sockets.

    

posted @ 2012-02-25 11:56  金陵书生  阅读(1110)  评论(0编辑  收藏  举报