1. AcceptEx 10061

客户端循环连接,没有发送数据,一定次数后,连接失败,WSAGetLastError的结果是10061。并且后续无法再次连接。

这是因为其中的一个参数,详细用法参考IOCP Input/Output Completion Port IO完成端口

BOOL AcceptEx(
SOCKET sListenSocket,
SOCKET sAcceptSocket,
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
);

dwReceiveDataLength

The number of bytes in lpOutputBuffer that will be used for actual receive data at the beginning of the buffer. This size should not include the size of the local address of the server, nor the remote address of the client; they are appended to the output buffer. If dwReceiveDataLength is zero, accepting the connection will not result in a receive operation. Instead, AcceptEx completes as soon as a connection arrives, without waiting for any data.

这里表示,如果设置了长度,那么AcceptEx就会在连接成功,并且接收到连接第一块数据的时候返回。那么就会有被攻击的风险,如果只有连接不发送数据,服务器投递的等待accept的socket就会被消耗光。并且客户端断开连接,GetQueuedCompletionStatus也没有返回,没有任何通知,除非自己主动查询连接的socket。出现这个现象后,客户端永远无法连接,提示报错10061:

WSAECONNREFUSED
10061
Connection refused.
No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running.

解决方法一

按照微软官方文档说明,把dwReceiveDataLength设置为0。

注意事项

void GetAcceptExSockaddrs(
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
sockaddr **LocalSockaddr,
LPINT LocalSockaddrLength,
sockaddr **RemoteSockaddr,
LPINT RemoteSockaddrLength
);

dwReceiveDataLength

The number of bytes in the buffer used for receiving the first data. This value must be equal to the dwReceiveDataLength parameter that was passed to the AcceptExfunction.

dwReceiveDataLength与AcceptEx的参数必须一致,所以这里也要填写0.

acceptExFunc(
listensocket,
iosocket,
wsabuf.buf,
,
sizeof(SOCKADDR_IN) + ,
sizeof(SOCKADDR_IN) + ,
&dwbytes,
&overlapped
))
{
if (WSA_IO_PENDING != WSAGetLastError())
{
ret = false;
}
}
getAcceptExSockFunc(
wsabuf.buf,
,
sizeof(SOCKADDR_IN) + ,
sizeof(SOCKADDR_IN) + ,
(LPSOCKADDR*)&localaddr,
&localaddrlen,
(LPSOCKADDR*)&clientaddr,
&clientaddrlen
);

这样可以获得地址信息,也可以避免连接没有发送数据的攻击。

解决方法二

If a receive buffer is provided, the overlapped operation will not complete until a connection is accepted and data is read. Use the getsockopt function with the SO_CONNECT_TIME option to check whether a connection has been accepted. If it has been accepted, you can determine how long the connection has been established. The return value is the number of seconds that the socket has been connected. If the socket is not connected, the getsockopt returns 0xFFFFFFFF. Applications that check whether the overlapped operation has completed, in combination with the SO_CONNECT_TIME option, can determine that a connection has been accepted but no data has been received. Scrutinizing a connection in this manner enables an application to determine whether connections that have been established for a while have received no data. It is recommended such connections be terminated by closing the accepted socket, which forces the AcceptEx function call to complete with an error.

int getsockopt(
SOCKET s,
int level,
int optname,
char *optval,
int *optlen
);

Parameters

s

A descriptor identifying a socket.

对应socket句柄。

level

The level at which the option is defined. Example: SOL_SOCKET.

需要获取的参数属于哪一个level分类。

optname

The socket option for which the value is to be retrieved. Example: SO_ACCEPTCONN. The optname value must be a socket option defined within the specified level, or behavior is undefined.

socket的对应属性。

optval

A pointer to the buffer in which the value for the requested option is to be returned.

返回对应数据的指针。

optlen

A pointer to the size, in bytes, of the optval buffer.

返回对应数据的长度。从这里可以看书,上面的数据必须我们自己申请,然后传递进去。

SO_CONNECT_TIME属于SOL_SOCKET。

SO_CONNECT_TIME DWORD

Returns the number of seconds a socket has been connected. This socket option is valid for connection oriented protocols only.

INT seconds;
INT bytes = sizeof(seconds);
int iResult = ; iResult = getsockopt( sAcceptSocket, SOL_SOCKET, SO_CONNECT_TIME,
(char *)&seconds, (PINT)&bytes ); if ( iResult != NO_ERROR ) {
printf( "getsockopt(SO_CONNECT_TIME) failed: %u\n", WSAGetLastError( ) );
exit();
}

另外一种解决方法就是,AcceptEx接收数据的参数不是0,但是需要定时通过getsockopt获取socket连接了多长时间,然后把没有发送数据并且超时的socket断掉。但是这个函数精确度是秒,设计起来也更复杂,不建议使用。

2. WSARecv/WSASend立即返回的处理

参考 https://tboox.org/cn/2018/08/16/coroutine-iocp-some-issues/

在开发过程中,看到过这两个函数介绍有可能立即返回,但是没有找到对应的用法,官方也没有特别说明,所以就没有在意。不过在心里也在考虑,如果立即返回,应该如何处理,是不是不用到工作线程处理了。

不管有没有处理,调用GetQueuedCompletionStatus的时候,系统都会返回。所以要么不处理,要么按照作者写的那样,做一个标识,处理过的在工作线程就不略不计即可。

3. GetQueuedCompletionStatusEx单请求慢

参考 https://tboox.org/cn/2018/08/16/coroutine-iocp-some-issues/

GetQueuedCompletionStatusEx可以一次请求多个,减少了调用和线程切换次数,增加了效率。但是作者测下来,如果是单一IO请求,GetQueuedCompletionStatusEx的效率反而慢了。具体原因不明,按照作者介绍,通过最近的IO请求动态的调整调用的函数。

4. 取消其他线程IO请求

参考 https://tboox.org/cn/2018/08/16/coroutine-iocp-some-issues/

CancelIO只能用于取消当前线程投递的io事件,想要在取消其他线程投递的io事件,需要使用CancelIOEx

IOCP陷阱的更多相关文章

  1. IOCP~~

    下载源代码 原文网址:http://www.codeproject.com/KB/IP/iocp_server_client.aspx 源码使用了高级的完成端口(IOCP)技术,该技术可以有效地服务于 ...

  2. IOCP Internals

    Buffer Type Buffer I/O 针对Buffer I/O的请求,系统会为其分配一个非换页内存作为缓存区,其大小等同于I/O请求的缓存区大小.对于写操作,I/O管理器在创建IRP时,将请求 ...

  3. 你可能不知道的陷阱, IEnumerable接口

    1.  IEnumerable 与  IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...

  4. java笔记--笔试中极容易出错的表达式的陷阱

    我相信每一个学过java的人儿们都被java表达式虐过,各种"肯定是它,我不可能错!",然后各种"尼玛,真假,怎么可能?",虽然在实际开发中很少会真的让你去使用 ...

  5. 【Swift】iOS UICollectionView 计算 Cell 大小的陷阱

    前言 API 不熟悉导致的问题,想当然的去理解果然会出问题,这里记录一下 UICollectionView 使用问题. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http://www.cn ...

  6. JavaScript中的this陷阱的最全收集

    JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值.事实并非如此,在JavaScript中,最 ...

  7. 高性能MySQL(四):schema陷阱

    一.schema陷阱 二.缓存表和汇总表 三.范式和反范式

  8. C#_闭包陷阱

    如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中. 即将for循环中的变量i修改成了引用闭包对象的公共变量i.这样一来,即使代码执行后离开了原局部变量i的 ...

  9. 安装 Linux 时碰到的硬盘分区的陷阱及应对

    硬盘分区的陷阱及应对 之所以想到写这篇,是因为本人在折腾 Linux 系统的过程中,有多次掉入硬盘分区的陷阱的经历.最近几天,再一次掉入坑中,折腾了两天才从坑中爬出来.经过多方查询资料,终于弄明白了硬 ...

随机推荐

  1. [NOI2019] 弹跳

    题意: 给你平面上的$n$个点,共有$m$个弹跳装置. 每个弹跳装置可以从点$p_{i}$以$t_{i}$的代价跳到矩形$(L_{i},D_{i}),(R_{i},U_{i})$中的任何一个点. 现在 ...

  2. 关于NB-IoT,没有比这篇更通俗易懂的啦!

    来源:内容来自「鲜枣课堂」,谢谢. 大家好,我是小枣君. 今天,我是来“吹NB”的.嗯,标题已经剧透了,这个NB,就是NB-IoT. 在过去的一年多,NB-IoT真的可以说是大红大紫.在通信圈里,除了 ...

  3. SpringBoot+Mybatis+Druid批量更新 multi-statement not allow异常

      本文链接:https://blog.csdn.net/weixin_43947588/article/details/90109325 注:该文是本博主记录学习之用,没有太多详细的讲解,敬请谅解! ...

  4. PIE SDK归一化水体指数法

    1.算法功能简介 归一化指数法(NDWI(Normalized Difference Water Index,归一化水指数)),用遥感影像的特定波段进行归一化差值处理,以凸显影像中的水体信息. 其表达 ...

  5. 49道spring面试题整理,附带答案

    1.选择使用Spring框架的原因? 使用Spring: 第一是使用它的IOC功能,在解耦上达到了配置级别. 第二是使用它对数据库访问事务相关的封装. 第三就是各种其他组件与Spring的融合,在Sp ...

  6. 教你玩转Linux系统目录结构

    Linux 内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的.Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POS ...

  7. spark源码解析--Shuffle输出追踪者--MapOutputTracker

    Shuffle输出追踪者--MapOutputTracker 这个组件作为shuffle的一个辅助组件,在整个shuffle模块中具有很重要的作用.我们在前面一系列的分析中,或多或少都会提到这个组件, ...

  8. mpvue 小程序开发之 数据埋点统计

    mpvue 小程序开发之 数据埋点统计 在开发过程中,有数据统计的需求,需要获取小程序当前页面和来源页面的数据,以及页面的停留时间 在对小程序api进行了一番研究之后,发现获取这些数据其实并不难 当前 ...

  9. iOS相关

    1. fastlane a collection of tools that help you automate building and releasing iOS and Android apps ...

  10. 集合(set)

    '''set是一个无序(不支持索引和切片)而且不重复的集合,有些类似于数学中的集合,也可以求交集,求并集等,''' s1={1,2,3,1} #定义一个set s1 如果s1={}为空则默认定义一个字 ...