当前位置:编程学习 > C/C++ >>

socket select

select模型的中心思想就是利用select函数,实现对I/O的管理。利用select函数,我们可以判断套接字上是否存在数据,或者能否向一个套接字写入数据。利用这个函数可以防止应用程序在一次I/O的绑定调用中进入锁定状态。

select函数原型:
int select(
  __in     int nfds,    //忽略
  __inout  fd_set *readfds,   //检查可读性
  __inout  fd_set *writefds,   //检查可写性
  __inout  fd_set *exceptfds,   //用于例外数据
  __in     const struct timeval *timeout //超时时间,传递NULL会无限期等待下去,0会立刻返回
);


可读性:
1.有数据可以读入。
2.连接已经关闭,重设或者中止。
3.加入调用了listen,而且一个连接正在建立,那么accept函数调用会成功。

关于连接已经关闭,重设或者中止的判断:
当一个套接字在调用了select之后具有可读性,那么这个时候我们可以通过调用recv获得数据。如果真的有数据发送过来,那么这个调用会成功。如果是关闭,重设或者中止,那么recv的调用会失败,这个时候通wsagetlasterror就可以判断连接是否已经中断。

可写性:
1.有数据可以发出。
2.如果已完成了对一个非锁定连接调用处理,连接就会成功。

对于可写性的检查,最好放在需要写数据的时候进行检查。如果和可读性放在同一个地方进行检查,那么select很可能每次都会因为可写性检查成功而返回。

例外数据:
1.加入已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
2.有带外数据可供读写。

fd_set的几个宏:
FD_SETSIZE    定义了fd_set所允许存放套接字的最大个数,默认是64。如果想修改这个默认值,那么在包含winsock2.h之前重新

定义这个宏。最大不要超过1024
FD_CLR(s, *set):从set中删除套接字s。
FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
FD_SET(s, *set):将套接字s加入集合set。
FDZERO(*set):将set初始化成空集合。

select调用流程:
1) 使用FDZERO宏,初始化自己感兴趣的每一个fd_set。
2) 使用FDSET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
3) 调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,它会修改每个fd_set结构,删除那些不存在待决I/O操作的套接字句柄
4) 根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。
5) 知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1 ),继续进行select处理。

下面是利用之前封装的socket类来实现的一个简单的select服务器,接受用户的连接和消息,在收到消息之后检查可写性,如果可写将收到的消息发还给用户:
 
Cpp代码 
#include <stdlib.h> 
#include <map> 
#include "../../common/winsock/sock_accepter.h" 
#include "../../common/winsock/sock_stream.h" 
#include "../../common/winsock/sock_init.h" 
#include "../../common/log/log.h" 
 
int main(int argc, char** argv) 

    SockInit init(2, 2); 
 
    SockAccepter accepter; 
    if(!accepter.StartListen("0.0.0.0", 5000)) 
    { 
        return 0; 
    } 
 
    fd_set read_set; 
    std::map<SOCKET, SockStream> users; 
 
    while (true) 
    { 
        FD_ZERO(&read_set); 
        FD_SET(accepter.GetHandle(), &read_set); 
        std::map<SOCKET, SockStream>::iterator ite = users.begin(); 
        for (; ite != users.end(); ++ite) 
        { 
            FD_SET(ite->first, &read_set); 
        } 
 
        int res = select(0, &read_set, NULL, NULL, NULL); 
        if (res == SOCKET_ERROR) 
        { 
            Log::Instance().WriteLog(LOG_ERROR, "select failed. err = %d", WSAGetLastError()); 
            break; 
        } 
        else 
        { 
            if(FD_ISSET(accepter.GetHandle(), &read_set)) 
            { 
                SockStream stream; 
                if(accepter.Accept(stream)) 
                { 
                    users.insert(std::make_pair(stream.GetHandle(), stream)); 
                } 
            } 
 
            for (unsigned int i = 0; i < read_set.fd_count; ++i) 
            { 
                 
                std::map<SOCKET, SockStream>::iterator ite = users.find(read_set.fd_array[i]); 
                if(ite == users.end()) 
                { 
                    continue; 
                } 
 
                char buffer[1025]; 
                int length = ite->second.Read(buffer, 1024); 
                if(-1 != length) 
                { 
                    buffer[length] = 0; 
                    Log::Instance().WriteLog(LOG_INFO, "recv msg = %s", buffer); 
                 

补充:软件开发 , C语言 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,