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语言 ,