• select()


    • One traditional way to write network servers is to have the main server block on accept(), waiting for a connection. Once a connection comes in, the server forks, then the child process handles the connection and the main server is able to service new incoming requests.
    • With select(), instead of having a process for each request, there is usually only one process that multiplexes all requests, servicing each request as much as it can.
    • So one main advantage of using select() is that your server will only require a single process to handle all requests.  Thus, your server will not need shared memory or synchronization primitives for different tasks to communicate.
    • As discussed before we can use the non-blocking sockets’ functions but it is CPU intensive???.
    • One major disadvantage of using select(), is that your server cannot act like there's only one client, like with a forking solution.  For example, with a forking solution, after the server forks, the child process works with the client as if there was only one client in the universe, the child does not have to worry about new incoming connections or the existence of other sockets.
    • With select(), the programming isn't as transparent.  The prototype is as the following:
              #include <sys/time.h>
              #include <sys/types.h>
              #include <unistd.h>
     
              int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
         
    DESCRIPTION 
           select()  allow  a  program  to monitor multiple file descriptors, waiting until one or more of the file descriptors
           become "ready" for some class of I/O operation (e.g., input possible).  A file descriptor is considered ready if it is  possible  to
           perform the corresponding I/O operation (e.g., read(2)) without blocking.
     
    Return value:
    On success, select() and pselect() return the number of file descriptors contained in the three returned  descriptor
           sets (that is, the total number of bits that are set in readfds, writefds, exceptfds) which may be zero if the time‐
           out expires before anything interesting happens.  On error, -1 is returned, and errno is set appropriately; the sets
           and timeout become undefined, so do not rely on their contents after an error.
    • The function monitors "sets" of file descriptors; in particular readfds, writefds, and exceptfds.  If you want to see if you can read from standard input and some socket descriptor, sockfd, just add the file descriptors 0 and sockfd to the set readfds.
    • The parameter numfds should be set to the values of the highest file descriptor plus one.  In this example, it should be set to sockfd+1, since it is assuredly higher than standard input that is 0.
    • When select() returns, readfds will be modified to reflect which of the file descriptors you have selected which is ready for reading.  You can test them with the macro FD_ISSET() listed below.
    • Let see how to manipulate these sets.  Each set is of the type fd_set.  The following macros operate on this type:
    1. FD_ZERO(fd_set *set) – clears a file descriptor set.
    2. FD_SET(int fd, fd_set *set) – adds fd to the set.
    3. FD_CLR(int fd, fd_set *set) – removes fd from the set.
    4. FD_ISSET(int fd, fd_set *set) – tests to see if fd is in the set.
    • select() works by blocking until something happens on a file descriptor/socket.  The 'something' is the data coming in or being able to write to a file descriptor, you tell select() what you want to be woken up by.  How do you tell it? You fill up an fd_set structure with some macros.
    • Most select() based servers look quite similar:
    1. Fill up an fd_set structure with the file descriptors you want to know when data comes in on.
    2. Fill up an fd_set structure with the file descriptors you want to know when you can write on.
    3. Call select() and block until something happens.
    4. Once select() returns, check to see if any of your file descriptors was the reason you woke up.  If so, 'service' that file descriptor in whatever particular way your server needs to (i.e. read in a request for a Web page).
    5. Repeat this process forever.
    • Sometimes you don’t want to wait forever for someone to send you some data.  Maybe every 60 seconds you want to print something like "Processing..." to the terminal even though nothing has happened.
    • The timeval structure allows you to specify a timeout period.  If the time is exceeded and select() still hasn’t found any ready file descriptors, it’ll return, so you can continue processing.
    • The struct timeval has the following fields:
              struct timeval
              {
                  int tv_sec;   /* seconds */
                  int tv_usec; /* microseconds */
              };
    • Just set tv_sec to the number of seconds to wait, and set tv_usec to the number of microseconds to wait.  There are 1,000,000 microseconds in a second.  Also, when the function returns, timeout might be updated to show the time still remaining.
    • Standard UNIX time slice is around 100 milliseconds, so you might have to wait that long no matter how small you set your struct timeval.
    • If you set the fields in your struct timeval to 0, select() will timeout immediately, effectively polling all the file descriptors in your sets.  If you set the parameter timeout to NULL, it will never timeout, and will wait until the first file descriptor is ready.
    • Finally, if you don’t care about waiting for a certain set, you can just set it to NULL in the call to select().
    • Now, some of you might think this is a great way to wait for data on a datagram socket and you are right: it might be.  Some Unices can use select() in this manner, and some can’t. You should see what your local man page says on the matter if you want to attempt it.
    • Some Unices update the time in your struct timeval to reflect the amount of time still remaining before a timeout.  But others do not. Don’t rely on that occurring if you want to be portable.  Use gettimeofday() if you need to track time elapsed.
    • When a socket in the read set closes the connection, select() returns with that socket descriptor set as "ready to read".  When you actually do recv() from it, recv() will return 0.  That’s how you know the client has closed the connection.
    • If you have a socket that is listen()ing, you can check to see if there is a new connection by putting that socket’s file descriptor in the readfds set.
    • The following code snippet waits 5.8 seconds for something to appear on standard input.
    /*selectcp.c - a select() demo*/
    #include <stdio.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    /* file descriptor for standard input */
    #define STDIN 0
     
    int main(int argc, char *argv[ ])
    {
    struct timeval tval;
    fd_set readfds;
    tval.tv_sec = 5;
    tval.tv_usec = 800000;
     
    FD_ZERO(&readfds);
    FD_SET(STDIN, &readfds);
    /* don’t care about writefds and exceptfds: */
    select(STDIN+1, &readfds, NULL, NULL, &tval);
    if (FD_ISSET(STDIN, &readfds))
     printf("A key was pressed lor! ");
    else
     printf("Timed out lor!... ");
     
    return 0;
    }
     
     
    FD_ISSET(i, &read_fds)
    select(nFdmax+1, &read_fds, NULL, NULL, 0)
     
    待验证select对read_fds是不是有重置???通过打印分析验证了该结论。
     
  • 相关阅读:
    SQL复制多表数据
    ie与firefox 关于js 的差别(转载)
    水晶报表函数大全【收藏】
    ArcGIS Engine对象库
    SQL Server死锁总结(转载)
    C#制作鹰眼全过程(带注释)
    大块鸭
    【经典】jQuery使用大全
    TreeView控件失效引发的思考
    根据数据集动态生成TREE
  • 原文地址:https://www.cnblogs.com/black-mamba/p/5678454.html
Copyright © 2020-2023  润新知