设为首页 加入收藏

TOP

socket select(一)
2014-11-23 23:24:04 来源: 作者: 【 】 浏览:1
Tags: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 timeva l *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
#include
#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 users;

while (true)
{
FD_ZERO(&read_set);
FD_SET(accepter.GetHandle(), &read_set);
std::map::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::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);
fd_set write_set;

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C开N次方的函数,全还给数学老师了 下一篇详解结构体、类等内存字节对齐

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: