Select模型

Winsock分别提供了“套接字模式”和“套接字I/O模型”,可对一个套接字上的I/O行为加以控制。注意:“套接字模式”和“套接字I/O模型”是无关的,套接字模型的出现是为了解决套接字模式存在的某些限制。


Winsock提供两种套接字模式:锁定和非锁定。

套接字I/O模型:Select,WSAAsyncSelect,WSAEventSelect,Overlapped I/O,Completion Port(完成端口)等

前一段时间已经对Winsock的一些基本函数进行了学习,估计和我一样的初学者已经都看过了这些函数,只是在应用上还存在一些问题,今天我只是在这里介绍一下Select模型,我所写内容也是借鉴别人的经验和别人的一些知识,有错误的地方还需各位指正。


Select模型:


这个模型的目的就是为了,防止应用程序在套接字处于锁定模式中时,在一次I/O绑定调用过程中,被迫进入锁定状态,同时又是防止在套接字处于非锁定状态时,产生WSAEWOULDBLOCK错误。

select函数:

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

其中,第一个参数nfds会被忽略。之所以仍然要提供这个参数,只是为了保持与早期的Berkeley套接字应用程序的兼容。大家可注意到三个fd _ set参数:一个用于检查可读性(readfds),一个用于检查可写性(writefds),另一个用于例外数据(exceptfds)。从根本上说,fb_set数据类型代表着一系列特定套接字的集合。

其中,readfds集合包括符合下述任何一个条件的套接字:
1.有数据可以读入。
2.连接已经关闭、重设或中止。
3.假如已调用了listen,而且一个连接正在建立,那么accept函数调用会成功。

writefds集合包括符合下述任何一个条件的套接字:
1.有数据可以发出。
2.如果已完成了对一个非锁定连接调用的处理,连接就会成功。


最后,exceptfds集合包括符合下述任何一个条件的套接字:

1.假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
2.有带外(out-of-band,OOB)数据可供读取。


例如,假定我们想测试一个套接字是否“可读”,必须将自己的套接字增添到readfds集合,再等待select函数完成。select完成之后,必须判断自己的套接字是否仍为readfds集合的一部分。若答案是肯定的,便表明该套接字“可读”,可立即着手从它上面读取数据。在三个参数中(readfds、writefds和exceptfds),任何两个都可以是空值( NULL);但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一个套接字句柄;否则, select函数便没有任何东西可以等待。最后一个参数timeout对应的是一个指针,它指向一个timeval结构,用于决定select最多等待I / O操作完成多久的时间。如tieout是一个空指针,那么select调用会无限期地“锁定”或停顿下去,直到至少有一个描述符符合指定的条件后结束。对timeval结构的定义如下:
Struct timeval
{
Long tv_sec;
Long tv_usec;
}

其中,tv_sec字段以秒为单位指定等待时间; tv_usec字段则以毫秒为单位指定等待时间。若将超时值设置为(0 ,0),表明select会立即返回,允许应用程序对select操作进行“轮询”。出于对性能方面的考虑,应避免这样的设置。select成功完成后,会在fd_set结构中,返回刚好有未完成的I/O操作的所有套接字句柄的总量。若超过timeval设定的时间,便会返回0。不管由于什么原因,假如select调用失败,都会返回socket_error。用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外FD_SET结构.

主要涉及到几个宏操作:
1. FD_CLR(s, *set):从s e t中删除套接字s。
2. FD_ISSET(s, *set):检查s是否s e t集合的一名成员;如答案是肯定的是,则返回T R U E。
3.FD_SET(s, *set):将套接字s加入集合s e t。
4.FD_ZERO ( * set ):将s e t初始化成空集合。


在书中提供了简单的一个socket的程序代码,在这里我写了多个终端的简单小程序可供参考:

WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
    return;
}

 


if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1 )
{
    WSACleanup( );
    return;
}


char hostname[256];
gethostname(hostname,sizeof(hostname));                     //这一代码是为
HOSTENT*hos=gethostbyname(hostname);                      //了实现自动获取安
CString CS=inet_ntoa(*(struct in_addr*)hos->h_addr_list[0]);// 装程序的主机代码
SOCKET socksrv = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in saServer;


//saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//宏INADDR_ANY定义的是0
saServer.sin_addr.S_un.S_addr = inet_addr(CS);//宏INADDR_ANY定义的是0
saServer.sin_family = AF_INET;
saServer.sin_port = htons(6000);
bind(socksrv, (SOCKADDR*)&saServer, sizeof(SOCKADDR));
SOCKET socketclientarray[FD_SETSIZE - 1];//定义套接字数组
listen(socksrv, 5);
for (int i=0; i<FD_SETSIZE; i++)
{
    socketclientarray[i] = INVALID_SOCKET;
}


int ret, nRet;
char buff[100];
memset(&buff, 0, sizeof(buff));
SOCKET clientsocket;

fd_set readfds;
FD_ZERO(&readfds);
FD_SET(socksrv, &readfds);

timeval tv;           //无限等待

tv.tv_sec = IFNINITE;

tv.tv_usec = 0;

 

for (;;)
{
    ret = select(0, &readfds, NULL, NULL, &tv);

 

   //不要判断ret是否大于0了,因为这是无限等待,返回时一定会有时间发生。
    if(FD_ISSET(socksrv, &readfds))
    {
        clientsocket = accept(socksrv, NULL, NULL);
        if(!insertsocket(socketclientarray, clientsocket))
        {
            closesocket(clientsocket);
            continue;
        }
    }

 

    //处理待决的套接字

    for(int j=0; j<FD_SETSIZE; j++)
    {

        if(FD_ISSET(socketclientarray[j], &readfds))

        {
            nRet = recv(socketclientarray[j], buff, sizeof(buff), 0);

            if(nRet == 0 || nRet == INVALID_SOCKET)
            {
                closesocket(socketclientarray[j]);
                socketclientarray[j] = INVALID_SOCKET;
                continue;
            }

            //对数据进行处理{.......}
            MessageBox(buff);
        }
    }

    FD_ZERO(&readfds);

    FD_SET(socksrv, &readfds);

    //将所有有效的套接字放入套接字句柄数组

    for(int idex=0; idex<FD_SETSIZE; idex++)
    {
    if(socketclientarray[idex] != INVALID_SOCKET)

        FD_SET(socketclientarray[idex], &readfds);

    }
}
}


BOOL insertsocket(SOCKET *psockarray, SOCKET sock)
{
for(int idex=0; idex<FD_SETSIZE; idex++)
{
    if(psockarray[idex] == INVALID_SOCKET)
    {
        psockarray[idex] = sock;
        break;
    }

    if(idex == FD_SETSIZE - 1)
        return FALSE;
}

    return TRUE;
}


       上面只是给简要的代码,有的辅助函数也没有给出。用select支持多Client是比较方便的.

 

本文转自:http://herocao.bokee.com/3593715.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值