异步非阻塞套接字避免了死循环的接收问题,但是软件用起来体验还是很差。究其原因,软件在指令的发送、接收上,

采取了一种不合理的方式:在指令的发送后,立刻调用接收函数,等待回令。

  若是采用同步阻塞套接字,那么如果连接出现问题没有回令,那么软件进程会始终等待锁死,这样显然是不合理。于是

采用同步非阻塞的接收方式,timeout被设置为NULL。这时会出现发令与回令的错位问题。这是由于,相应回令没有及时到

达,而接收函数已经返回,第一条发令被当做无回令处理;而此时回令被堆积在了缓冲区,被第二条发令接收到,造成指令错位。

  实际上,这种同步的接收方式是不合理的。如果软件有长连接中接收消息的可能性,那么显然单独设置接受线程是更为合理,

这种主动的接受方式优于被动的调用接收函数的方式。在软件的设计中,通信需要下一番功夫,这是个教训。

  总之,软件的设计已经无法更改,我们需要缓解这个问题,所以只好采用同步非阻塞的结束方式,使用select()函数实现。

  以下内容引用自http://blog.csdn.net/inspiron_110/article/details/4764847

  select模型(选择模型)

  先看一下下面的这句代码:

  int iResult = recv(s, buffer,1024);

  这是用来接收数据的,在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把数据读到

buffer里后recv函数才会返回,不然就会一直阻塞在那里。在单线程的程序里出现这种情况会导致主线程(单线程程序里只有

一个默认的主线程)被阻塞,这样整个程序被锁死在这里,如果永远没数据发送过来,那么程序就会被永远锁死。这个问题可以

用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差。Select模型就是为了解决这个问题而出现的。

  再看代码:

  int iResult = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);

  iResult = recv(s, buffer,1024);

  这一次recv的调用不管套接字连接上有没有数据可以接收都会马上返回。原因就在于我们用ioctlsocket把套接字设置为非阻塞模式了。

不过你跟踪一下就会发现,在没有数据的情况下,recv确实是马上返回了,但是也返回了一个错误:WSAEWOULDBLOCK,意思就是请

求的操作没有成功完成。看到这里很多人可能会说,那么就重复调用recv并检查返回值,直到成功为止,但是这样做效率很成问题,开销太大。

  感谢天才的微软工程师吧,他们给我们提供了好的解决办法。

  先看看select函数

  int select(

    int nfds,

    fd_set FAR *readfds,

    fd_set FAR *writefds,

    fd_set FAR *exceptfds,

    const struct timeval FAR *timeout

  );

  第一个参数不要管,会被系统忽略的。第二个参数是用来检查套接字可读性,也就说检查套接字上是否有数据可读,同样,第三个参数

用来检查数据是否可以发出。最后一个是检查是否有带外数据可读取。

  参数详细的意思请去看MSDN,这里限于篇幅不详细解释了。

  最后一个参数是用来设置select等待多久的,是个结构:

  struct timeval {

    long tv_sec; // seconds

    long tv_usec; // and microseconds

  };

  如果将这个结构设置为(0,0),那么select函数会马上返回。

  说了这么久,select的作用到底是什么?

  他的作用就是:防止在在阻塞模式的套接字里被锁死,避免在非阻塞套接字里重复检查WSAEWOULDBLOCK错误。

  他的工作流程如下:

  1:用FD_ZERO宏来初始化我们感兴趣的fd_set,也就是select函数的第二三四个参数。

  2:用FD_SET宏来将套接字句柄分配给相应的fd_set。

  3:调用select函数。

  4:用FD_ISSET对套接字句柄进行检查,如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么

说明马上可以进行相应的IO操作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的

fd_set里,那么说明当前数据已经来了,马上可以读取成功而不会被阻塞。

最后附上一段示例代码:

    WSADATA wsa;
WSAStartup(MAKEWORD(,), &wsa);
    
SOCKET fdServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(InternetAddr);
server.sin_port = htons(iPort);
    
int ret = bind(fdServer, (sockaddr*)&server, sizeof(server));
ret = listen(fdServer, ); SOCKET AcceptSocket;
fd_set fdread;
timeval tv;
int nSize; while()
{
FD_ZERO(&fdread);//初始化fd_set
FD_SET(fdServer, &fdread);//分配套接字句柄到相应的fd_set
        
//这里我们打算让select等待两秒后返回,避免被锁死
tv.tv_sec = ;
tv.tv_usec = ;
        
select(, &fdread, NULL, NULL, &tv);
        
nSize = sizeof(server); //如果套接字句柄还在fd_set里,说明客户端已经有
connect的请求发过来了,马上可以accept成功
if (FD_ISSET(fdServer, &fdread))
{
AcceptSocket = accept(fdServer,
( sockaddr*) &server, &nSize);
    break;
   }
} char buffer[];
ZeroMemory(buffer, ); //这里同样可以用select,用法和上面一样
ret = recv(AcceptSocket,buffer,,); closesocket(AcceptSocket);
WSACleanup();
return;

C++ 非阻塞套接字的使用 (3)的更多相关文章

  1. python_非阻塞套接字及I/O流

    http://www.cnblogs.com/lixy-88428977/p/9638949.html 首先,我们要明确2个问题: 普通套接字实现的服务端有什么缺陷吗? 有,一次只能服务一个客户端! ...

  2. 非阻塞套接字编程, IO多路复用(epoll)

    非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...

  3. 非阻塞套接字与IO多路复用

    我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个客户端! 并且,为了使一个客户端能够不断收发消息,我们还要使用while循环来轮询,这极大地降低了我们的效率 acce ...

  4. 11 非阻塞套接字与IO多路复用(进阶)

    1.非阻塞套接字 第一部分 基本IO模型 1.普通套接字实现的服务端的缺陷 一次只能服务一个客户端! 2.普通套接字实现的服务端的瓶颈!!! accept阻塞! 在没有新的套接字来之前,不能处理已经建 ...

  5. 非阻塞套接字与IO多路复用(转,python实现版)

    非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回.epoll工作在非阻塞模式时,才会发挥作用. 我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个 ...

  6. C++ 非阻塞套接字的使用 (2)

    继续话题——软件中的异步非阻塞通讯方式. 由于软件基于MFC开发,所以实现异步通讯时使用了CAsyncSocket类. 首先要了解CAsyncSocket异步机制,引用自 http://blog.cs ...

  7. C++ 非阻塞套接字的使用 (1)

    在维护代码的过程中,发现软件运行的CPU占用率居高不下,在4核的电脑上占用了25%的CPU.查阅资料的得知,这是可能是由于软件中出现了死循环. 经过对软件的一些测试,最终确定了死循环出现的位置——通讯 ...

  8. 阻塞套接字返回EAGAIN

    今天用NDK写了一个通信程序,发现阻塞SOKCET 读写的时候返回了EAGAIN.NDK下PERROR输出为Try Again.查了半天头文件 在网上找到了原因.在此纪录.网址为http://blog ...

  9. 非阻塞SOCKET套接字connect等待时间的实现

     SOCKET cClient;     cClient=socket(AF_INET,SOCK_STREAM,0);     //设置为非阻塞套接字     int iMode = 1;     i ...

随机推荐

  1. 在css嵌套中的html的table里的字左右不对齐

    [现象]AAAA与天数的数字左右不居中 <table border=1 align="center"> <tr> <td width="20 ...

  2. JavaScript导出excel文件实现

    function getXlsFromTbl(inTblId, inWindow, fname) { try { var allStr = ""; var curStr = &qu ...

  3. JS--垒房子

    垒房子的小游戏,还没加上得分选项,这是自己的练习笔记,留作笔记随时查看. <!DOCTYPE html><html>    <head>        <me ...

  4. UIImagePickerControllerDelegate---ActionSheet---获得设备型号

    <pre name="code" class="java">//IOS主要用的是UIImagePickerControllerDelegate这个事 ...

  5. hdu 5876 (补图BFS) Sparse Graph

    题目:这里 题意: 相当于一开始给一个初始好了的无向完全图给你,然后给让你删除m条边,再给你一个点v,最后问你在剩下的图里从这个点v出发能到达所有边点的最小路径是多少? 一看是所有点的最小路径,一看就 ...

  6. 用c和c++的方式实现栈

    #include <stdio.h> #include <stdlib.h> #include <assert.h> struct LinkNode { int d ...

  7. android opengl es代码功能

    /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Versi ...

  8. win10下搭建QTP测试环境

    安装环境win 10 64位企业版 个人学习用1..net 3.5无法安装更新问题解决:打开windows update 服务2.win10 安装中提示为了对电脑进行保护,已经阻止此应用,请与管理员联 ...

  9. [linux]windows无法访问samba的安全性问题(关闭selinux)

    背景 在某一天重启了虚拟机的 linux 之后,我的 windows 在连接上 samba 之后,点击某些文件夹的时候,会出现没有权限打开的情况.这问题折腾了我一度重新配置了好几次 samba 的配置 ...

  10. 获取当前运行dll文件的路径

    char moduledir[MAX_PATH];  GetModuleFileNameA(GetModuleHandleA("ppdl_BE081_BIW_seal_library.dll ...