首页 > 代码库 > 用 select 实现处理多连接的异步通讯服务器

用 select 实现处理多连接的异步通讯服务器

因为对于任何句柄 ( file descriptor ) select 函数都能检测出其状态变化,对于用于 listen 的 socket 也是一样。

只要把用于 listen 的 socket 加入 ( FD_SET ) 到 select 检测的集合里,当有连接到来时 select 就能判断到。因为 select 函数能处理的是句柄集合,所以加入这样一个 socket 并不与 select 判断其它 socket 的状态有任何冲突。

  • 实现片段代码:

#define MAX_NUM_ONLINE_SOCKET 100

int lsn_sock_fd;

int client_sock_fd[MAX_NUM_ONLINE_SOCKET];

fd_set rdfds;

int maxfds;

int tmp_fd;

struct time tv;

int retval;

int i;

 

maxfds = -1;

for(i = 0; i < MAX_NUM_ONLINE_SOCKET; i++) client_sock_fd[i] = -1;

 

lsn_sock_fd = socket(AF_INET, SOCK_STREAM, 0);

 

/* 设置监听的地址和端口 */

/* 调用 bind 把地址信息与 socket 绑定到一起 */

 

listen(lsn_sock_fd, 2);

 

while(1) {

    maxfds = 0;

    FD_ZERO(&rdfds);

    if(lsn_sock_fd > 0) {

        FD_SET(lsn_sock_fd, &rdfds); /* 在这里把用于 listen 的 socket 加入到 select 判断的句柄集合里来 */

        if( lsn_sock_fd > maxfds) maxfds = lsn_sock_fd;

    }
    for(i = 0; i < MAX_NUM_ONLINE_SOCKET; i++) {

        if(client_sock_fd[i] > 0) {

          FD_SET(client_sock_fd[i], &rdfds); /* 把所有在线客户端连接加入到 select 检测的集合中来 */

      if( client_sock_fd[i] > maxfds) maxfds = client_sock_fd[i];

        }

    }

    tv.tv_sec = 1;

    tv.tv_usec = 0;

    if(maxfds < 1) continue;

    retval = select(maxfds + 1, &rfds, NULL, NULL, &tv);
    if(retval < 0) {

        printf("select 出错,错误编号:%d,错误信息:%s\n", errno, strerror(errno));

        break;

    }

    else if(!retval) continue; /* listen 的 socket 上没有任何连接到来,在线的所有连接上也没有任何一个上有数据到来 */

 

    if(FD_ISSET(lsn_sock_fd, &rdfds)) { /* 说明 listen 的 socket 上有新的连接到来了 */

        tmp_fd = accept(lsn_sock_fd, ......);

        for(i = 0; i < MAX_NUM_ONLINE_SOCKET; i++) {

            if(client_sock_fd[i] < 0) {

                client_sock_fd[i] = tmp_fd;

                break;

}

        }

         if(i == MAX_NUM_ONLINE_SOCKET) {

                printf("连接太多了,我最多只能接受%d个连接同时在线\n", MAX_NUM_ONLINE_SOCKET);  

                close(tmp_fd);

         }
    }
    for(i = 0; i < MAX_NUM_ONLINE_SOCKET; i++) {

        if(FD_ISSET(client_sock_fd[i], &rdfds)) { /* 说明第 i 个客户端连接上有数据到来了 */

             recv(client_sock_fd[i], ......);
             /* 对这个连接进行其它操作 */
             send(client_sock_fd[i], ......);

             /* 如果这个连接处理完毕了就要 close 它,并执行 client_sock_fd[i] = -1 ; */
        }

}

}

From:http://hi.baidu.com/beibeiboo/item/3b6e0ecb7e6d0f50ac00ef7a