由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续。

如果想改变这种一直等下去的焦急状态,可以多线程来实现(不再等待,同时去recv,同时阻塞),每个socket连接使用一个线程,这样效率十分低下,根本不可能应对负荷较大的情况(是啊,占用各种资源,电脑啊,你耗不起)。

这时候我们便可以采取select模型。select允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或多个事件发生或经历一段指定时间后才唤醒它。select告诉内核对哪些描述子(文件描述符)感兴趣以及等待多长时间。这就是所谓的非阻塞模型,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况。如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高。

select的函数原型:

int select
(
int nfds, //Winsock中此参数无意义
fd_set* readfds, //进行可读检测的Socket
fd_set* writefds, //进行可写检测的Socket
fd_set* exceptfds, //进行异常检测的Socket
const struct timeval* timeout //非阻塞模式中设置最大等待时间
)

  第一种解释版本:

/*参数列表
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
  
fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
  
fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
  
fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
  
struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
*/

  OS内核通过查看读或者写数据缓冲区是否有数据类决定select的返回值。

/*
返回值: 负值:select错误 正值:某些文件可读写或出错,即使是出错了,但是调用recv函数的时候不会阻塞了 0:等待超时,没有可读写或错误的文件
*/

  第二种解释版本:

参数解释:

参数1 nfds:这个参数是一个被忽略的参数,我们在程序中传入0即可,其目的是与伯克利套接字兼容。

参数2 readfds:可读性监视集合,可读性指有连接到来、有数据到来、连接已关闭、重置或终止。

参数3 writefds:可写性监视集合,可写性指数据可以发送、连接可以成功。

参数4 exceptfds:例外性监视集合,例外性指连接会失败、外带数据到来

参数4 timeout:该参数指定select会等待的时间,者结构很简单,要是有兴趣可以看看msdn

注意:select参数中的三个监视集合至少有一个不能为空,任何其它两个都可为空

  


fd_set的结构,这个结构是用来装SOCKET的,把要监视SOCKET传给这个结构,然后同select函数进行监视。

struct fd_set
{
u_int fd_count; // how many are SET?
SOCKET fd_array[FD_SETSIZE]; // an array of SOCKETs
} ;

  和select模型紧密结合的四个宏:

FD_CLR( s,*set) 从队列set删除句柄s。

FD_ISSET( s, *set) 检查句柄s是否存在与队列set中。

FD_SET( s,*set )把句柄s添加到队列set中。

FD_ZERO( *set ) 把set队列初始化成空队列。

  select选择模式倚赖于select函数,其思想就是让select函数对传入fd_set进行监视(fd_set中装有你的SOCKET句柄),如果没什么事发生select就将fd_set中的SOCKET清除。

timeval outTime;
outTime.tv = 1; //设置等待时间为1s
outTime.usec = 0; //毫秒
fd_set fdread;
while(true)
{
FD_ZERO(&fdread);
FD_SET(sessionSock, &fdread) //sessionSock为之前创建的会话套接字
select(0, &fdread, NULL, NULL, &outTime);
if(FD_ISSET(sessionSock, &fdread))//判断套接字是否还在集合中
{
recv_cnt = recv(sessionSock, buf, bufSize, 0);
}
else
{
//没有数据写入,进行其他操作
}
}

  结束了吗,有问题吗?是否有人觉得奇怪,我这说的是异步I/O,但这个select依然会阻塞进程啊,不是设置了outTime的吗?此问题很好,select本身是会阻塞的,我们可以使用select实现阻塞式套接字(如上),也可以实现异步套接字。我个人对实现异步套接字的理解是:你可以单独使用一个线程来进行你select,也就是说select阻塞你单独的线程,说白了就是让线程来完成异步。


下面具体分析一下项目中的socket的select模型:

对于工具机的select模型,它作为服务器只有一个socket,但是也用select模型,这样可以避免recv()函数阻塞:

while(ros::ok())
{
if(!connected)
{
ROS_INFO("Restarting connection..."); if(serverSocket > 0)
{
close(serverSocket);
serverSocket = -1;
} if(clientSocket > 0)
{
close(clientSocket);
clientSocket = -1;
} bool re;
if(bServer)
re = startServer();
else
re = connectServer();
if(!re)
continue;
}//if
int len =0;
int revDateCount = 0; int re = selectSocket(clientSocket, 100); if(re < 0)
{
ROS_ERROR("Receive Thread Select Error!");
connected = false;
continue;
}
else if(re == 0)
{
continue;
}
else//re > 0
{
len = recv(clientSocket, (void*)recvBuffer, MAX_BUFFER_LEN, 0);
if(len > 0)
{
memcpy((void*)(buff + revDateCount), (void*)recvBuffer, len);
revDateCount += len;
}//if
else
{
ROS_ERROR("Receive Data Error!");
connected = false;
goto __reconnect__;
}//else
}// re > 0 __reconnect__:;
}//while(ros::ok())

  其中的selectSocket函数如下,fd_set中只有一个描述符

int CSocket::selectSocket(int &hSocket, int msec, int sec, bool bRead)
{
fd_set fdset;
struct timeval tv;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
msec = msec > 1000 ? 1000 : msec;
tv.tv_sec = sec;
tv.tv_usec = msec * 1000; int iRet = 0;
if ( bRead ) {
iRet = select(hSocket + 1, &fdset, NULL , NULL, &tv);
}else{
iRet = select(hSocket + 1, NULL , &fdset, NULL, &tv);
}
return iRet;
}

  利用select的模式,虽然在工控机的服务器端只有一个socket,但是select模式能够很好的在不阻塞线程的情况下,调用recv函数。

再看云台角度查询的socket客户端:

socketMutex.Lock();
byte data[7] ={0xFF, 0x01,0x00, 0x51, 0x00, 0x00, 0x52};
if(sendData(data, sizeof(data), false) == 7)
{
Sleep(50);
while(1)
{
FD_ZERO(&readfds);
FD_SET(clientSocket, &readfds);
time.tv_sec = 0;
time.tv_usec = 500000;
re = select(nfds, &readfds, NULL, NULL, &time); if (re > 0)
{
num = recv(clientSocket, (char*)buffer, 128, 0);
if(num == 7)
{
p=buffer[4];
p = (p<<8);
p += buffer[5];
break;
}
else if (num <= 0)
{
bConnect = FALSE;
ErroFunc(_T("连接丢失"));
goto __reConnect__;
}
else
{
ErroFunc(_T("丢包"));
}
}//if
else
{
bConnect = FALSE;
ErroFunc(_T("连接丢失,正在重连..."));
socketMutex.Unlock();
goto __reConnect__;
}//else }//while }//if
socketMutex.Unlock();

  在select返回异常的时候,直接重新连接,不调用recv。

socket select模型的更多相关文章

  1. socket select()模型

    转载:http://www.cnblogs.com/xiangshancuizhu/archive/2012/10/05/2711882.html 由于socket recv()方法是阻塞式的,当有多 ...

  2. C# Socket select模型

    http://www.cnblogs.com/Clingingboy/archive/2011/07/04/2097806.html http://www.cnblogs.com/RascallySn ...

  3. u3d局域网游戏网络(c# socket select 模型)——续

    原文:http://www.cnblogs.com/saucerman/p/5555793.html 因为项目要加语音.语音数据都非常大.所以顺带就把之前写的网络模块一起测试了. 然后发现了一些bug ...

  4. u3d局域网游戏网络(c# socket select 模型)

    之前写了一篇. 发完之后第二天实际应用到游戏之后还是发现了一些小毛病. 比如网络模块有重复使用(多对象)的情况.所以将静态类该成了普通类. 比如安卓下会有些异常出现导致游戏逻辑不正常.所以网络相关的函 ...

  5. c# socket select 模型代码(u3d)

    其实写过多次网络链接.但是因为换了工作,又没电脑在身边,所以以前的代码都没办法翻出来用. 所以从今天起,一些常用的代码只好放到网上. 公司有一个局域网的游戏.本来想用u3d的rpc就可以完成.但是后来 ...

  6. windows socket编程select模型使用

    int select(         int nfds,            //忽略         fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性       ...

  7. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  8. socket之 select模型

    前段时间一直想学习网络编程的select模型,看了<windows网络编程>的介绍,参考了别人的博客. 这里的资料主要来自http://www.cnblogs.com/RascallySn ...

  9. 很幽默的讲解六种Socket IO模型 Delphi版本(自己Select查看,WM_SOCKET消息通知,WSAEventSelect自动收取,Overlapped I/O 事件通知模型,Overlapped I/O 完成例程模型,IOCP模型机器人)

    很幽默的讲解六种Socket IO模型(转)本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型 二:WSAAsyncSel ...

随机推荐

  1. LCD与ARM,具体

    一  实验内容简要描写叙述 1.实验目的 学会驱动程序的编写方法,配置S3C2410的LCD驱动,以及在LCD屏上显示包含bmp和jpeg两种格式的图片 2.实验内容  (1)分析S3c2410实验箱 ...

  2. Repeater获取某一行TextBox值

    TextBox tb = (TextBox)e.Item.FindControl("TextBoxID");

  3. 20151113--JSTL

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  4. Linux学习之scp命令

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的,可能会稍微影响一下速度.当你服务器 ...

  5. Git 系列(四):在 Git 中进行版本回退

    在这篇文章中,你将学到如何查看项目中的历史版本,如何进行版本回退,以及如何创建 Git 分支以便你可以大胆尝试而不会出现问题. 在你的 Git 项目的历史中,你的位置就像是摇滚专辑中的一个片段,由一个 ...

  6. Vim配置 终端背景色配置

    在终端下使用vim进行编辑时,默认情况下,编辑的界面上是没有显示行号.语法高亮度显示.智能缩进 等功能的.为了更好的在vim下进行工作,需要手动设置一个配置文件:.vimrc.在启动vim时,当前用户 ...

  7. centos之jdk安装

    1.环境说明 系统:centos6.7, 2.6.32-573.el6.x86_64; jdk:jdk1.7.0_80 2.下载jdk并上传      下载jdk-7u80-linux-x64.tar ...

  8. matlab 相关性分析

    Pearson相关系数 考察两个事物(在数据里我们称之为变量)之间的相关程度,简单来说就是衡量两个数据集合是否在一条线上面.其计算公式为: 或或 N表示变量取值的个数. 相关系数r的值介于–1与+1之 ...

  9. 使用MySQL处理百万级以上数据时,不得不知道的几个常识

    最近一段时间参与的项目要操作百万级数据量的数据,普通SQL查询效率呈直线下降,而且如果where中的查询条件较多时,其查询速度简直无法容忍.之前数据量小的时候,查询语句的好坏不会对执行时间有什么明显的 ...

  10. Nand ECC校验和纠错原理及2.6.27内核ECC代码分析

    ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法.如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个 ...