好久没来了,散分!同时,也顺道问个有点难度的问题…………

shenyi0106 2012-03-22 01:18:10
最近在搞嵌入式linux防火墙系统,其中涉及到一个测试流量的问题,所以写了个UDP的发包工具,用IOCP实现的,
本来以为可以加快发包速度,可是万万没想到出了个莫名其妙的问题:

先说说我所使用的技术情况:
我是预先分配了64个大小为1024个字节的缓冲区充当我的发送缓冲池,用一个UINT64的变量充当“哨兵”,每一位代表一个缓冲区的可用或者不可用。每次要发送数据时,都是检查哨兵的对应bit位,然后从缓冲池的对应bit位中获得一个缓冲区,然后在对这个缓冲区进行填充数据,在然后使用WSASendTo进行投递发送。在IOCP收到完成通知后,去更新上面说到得那个“哨兵”位,使其所对应的缓冲区变为可用状态。

整个过程中,“哨兵”的更新是同步进行的,不存在脏数据问题。

现在问题来了:
使用WSASendTo投递出去的UDP包(仅是测试流量,不需要测试分片,所以包大小都是<=1024个字节),开始流量很小时(估计网卡还可以处理的过来),一切正常,

获得发送缓冲区—》填充数据—》WSASendTo投递—》GCQS获得完成通知—》更新“哨兵”位—》获得发送缓冲区。

当我增大流量(100Mbps打满)时,过了没一小会(估计也就几秒钟的时间),流量一下变成0了。通过检查发现,发送线程始终停留在检查“哨兵”的这个过程上,因为它发现哨兵始终是0(也就是说,因为始终没获得发送缓冲区,而不进行发送)。通过进一步检查发现,当发送速度过快(快于网卡物理发送速度时),网卡开始丢包,一旦丢包,IOCP将得不到完成通知,“哨兵”位始终没有被释放,致使发送线程始终获得不到发送缓冲区。

好了,问题浮出水面了:
UDP在IOCP模型下,如果发送速度超过网卡速度,导致网卡丢包,那么它所发送的数据将得不到完成通知,导致投递出去的缓冲区始终被内存锁定(成为非分页内存),最终因内存暴涨,导致程序崩溃,请问各位遇到过这个问题的人,有没有好的解决办法?


备注: 通过增加发送超时来判断的方法我已经实现过了,最终会导致我上面所说的情况,就是内存被不断的锁定,非分页内存会不断的增长,最终程序会崩溃。
...全文
504 86 打赏 收藏 转发到动态 举报
写回复
用AI写文章
86 条回复
切换为时间正序
请发表友善的回复…
发表回复
Kevin_qing 2012-03-26
  • 打赏
  • 举报
回复

if里面的写法也不好

if(WSASendTo(..........))
{
if(ERROR_IO_PENDING!=WSAGetLastError()){
//所有错误处理,删buf,关socket
}
}else
{
//马上完成,什么都不用做。
}

fishion 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 75 楼 shenyi0106 的回复:]

引用 73 楼 fishion 的回复:
C/C++ code
//发送数据
if(WSASendTo(hSock, &amp;(PerIoData->DataBuf), 1, &amp;dwSendByts, dwFlags,
(PSOCKADDR)&amp;(PerIoData->From), sizeof(PerIoData->From),&amp;……
[/Quote]
SOCKET_ERROR和ERROR_IO_PENDING同时出现时,在TCP中就得关闭套节字和释放内存了,在UDP下就不知道是不是一样
fishion 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 76 楼 shenyi0106 的回复:]

引用 74 楼 fishion 的回复:
引用 50 楼 shenyi0106 的回复:

如果按照李兄所说的方法,投递一个,等通知了,在投递另一个,这样是绝对没有任何问题,也不会因产生丢包而导致的无完成通知现象(在多socket同时发送的情况下,估计就算这样也会存在问题),但是这样就没有效率可言了,无法充分利用1000M网卡(所以在1000M网卡上你也才打600M,我的目标是900M~1……
[/Quote]
但你的代码里给我的理解就是,一个 OVERLAPPED 能同时被使用,但这却是不可以的,一个OVERLAPPED只能在IO操作完成后才能再使用
Kevin_qing 2012-03-26
  • 打赏
  • 举报
回复
你没看出来1234和998就是因为你的overlapped和address被破坏了才出现的么
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 68 楼 shenyi0106 的回复:]
此问题已解决,最终原因是没有处理1234和998这两个GetLastError消息导致的。
非常谢谢各位的热情参与和讨论,我将会在周一下班前平均散掉这300分,以答谢各位的帮忙
[/Quote]

此问题我已经解决,在处理完1234和998这两个错误消息后,就已经没有问题了(100M打满,127.0.0.0回环打了400M,跑了30分钟)。

但是就“直接发送成功,也会有完成通知”这事,我之前的测试结果的确是“直接成功后,没有完成通知”,但是为了验证一下这个结果,我将在稍后在测试一下。

再次谢谢各位的关注……
Kevin_qing 2012-03-26
  • 打赏
  • 举报
回复
你把else里面的东西删掉再试就行了。 怎么越扯越远
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 74 楼 fishion 的回复:]
引用 50 楼 shenyi0106 的回复:

如果按照李兄所说的方法,投递一个,等通知了,在投递另一个,这样是绝对没有任何问题,也不会因产生丢包而导致的无完成通知现象(在多socket同时发送的情况下,估计就算这样也会存在问题),但是这样就没有效率可言了,无法充分利用1000M网卡(所以在1000M网卡上你也才打600M,我的目标是900M~1000M,甚至是双向的),所以我才想用IOCP……
[/Quote]
我用的是多个IO投递,并不是一个IO投递用多次,概念不一样。
通俗的将,就是一个公交卡多个人共用,或者有多个公交卡,每个人都用自己的,互相不冲突
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 73 楼 fishion 的回复:]
C/C++ code
//发送数据
if(WSASendTo(hSock, &(PerIoData->DataBuf), 1, &dwSendByts, dwFlags,
(PSOCKADDR)&(PerIoData->From), sizeof(PerIoData->From),&(PerIoData->Overlapped), NU……
[/Quote]
这里的处理只是直接发送成功的处理,接收到完成通知后,还有类似的处理。当然这块还需要测试,到底直接发送成功是否还有完成通知,我稍后会给出测试结果。

呵呵,SOCKET_ERROR和ERROR_IO_PENDING同时出现时,是不能释放的,它表示数据包正在处理,需要等待完成通知,你可能说错了(是不是想说返回SOCKET_ERROR,但是GetLastError不是ERROR_IO_PENDING的情况?)
fishion 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 50 楼 shenyi0106 的回复:]

如果按照李兄所说的方法,投递一个,等通知了,在投递另一个,这样是绝对没有任何问题,也不会因产生丢包而导致的无完成通知现象(在多socket同时发送的情况下,估计就算这样也会存在问题),但是这样就没有效率可言了,无法充分利用1000M网卡(所以在1000M网卡上你也才打600M,我的目标是900M~1000M,甚至是双向的),所以我才想用IOCP这个模型来试试。
[/Quote]

IOCP的效率不是这样体现的,得等待IO操作完成了才能再重新利用那个IO的
fishion 2012-03-26
  • 打赏
  • 举报
回复
 //发送数据
if(WSASendTo(hSock, &(PerIoData->DataBuf), 1, &dwSendByts, dwFlags,
(PSOCKADDR)&(PerIoData->From), sizeof(PerIoData->From),&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
//如果不是“设备忙”错误,则关闭SOCKET,并删除与之相关的结构
if (WSAGetLastError() != ERROR_IO_PENDING)
{
CSockData::GetInstance()->PushIn(hSock, PerIoData);
return ERR_SNDFAIL;
}
}
//直接发送成功
else
{
CSockData::GetInstance()->PushIn(hSock, PerIoData);
}


这里你都是用了IO投递的,那么IO的清空操作应该是在 GetQueuedCompletionStatus线程里的,而不应该是在
//直接发送成功
else
{
CSockData::GetInstance()->PushIn(hSock, PerIoData);
}

中的

还有就是SOCKET_ERROR,ERROR_IO_PENDING同时出现时,应该关闭那个套节字,让系统释放空间。
UDX协议 2012-03-26
  • 打赏
  • 举报
回复
问题已经非常明朗了,Kevin_qing。
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
恩,谢谢,我将仔细的再次测试一下,以确定到底有没有通知。

结果会再这周内公布
昨夜无风 2012-03-26
  • 打赏
  • 举报
回复
挺好的问题,大家共同努力,

IOCP的使用还是返回OI信息后再处理!

效率和鲁棒性需要找一个平衡!
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
[Quote=引用 84 楼 wwwllg 的回复:]
本质就是这问题,还说无关。无语。
[/Quote]

那看来你只能继续“无语”了……
UDX协议 2012-03-26
  • 打赏
  • 举报
回复
本质就是这问题,还说无关。无语。
shenyi0106 2012-03-26
  • 打赏
  • 举报
回复
非常感谢各位……
看来是我记错了,直接发送成功后,的确还会有完成通知。

但是这个问题只是个潜在的BUG,和我要咨询的问题应该无关。
因为我发送的包都是>=1024的,经过WSASendTo时都没有“立即完成”的,全部都是通过完成通知回调的。
所以,它确实是存在的问题,但不是引起我所提出问题的前因。

不过,不管如何,在处理的1234和998后,程序没有在出现任何问题,所以应该算是解决了此问题,感谢各位的鼎立相助,并且不厌其烦的指出存在的不足。

开始散分……
Kevin_qing 2012-03-25
  • 打赏
  • 举报
回复
一个很简单的测试就可以知道立即完成时是否触发iocp.

在你成功分配和释放的地方记下数,

然后看压力大的情况下,计数是否会归0

出现<0的情况就说明你代码错了

我以前在xp上测试说明是会触发iocp 的,然后现在在win7/2003下面用,计数也是正常,也就是说和xp一样

Kevin_qing 2012-03-25
  • 打赏
  • 举报
回复
ls的。。。。。。你返回值都没看清楚

你的if里面是失败,else是成功。

if里面没有问题的,开始我没看清楚,
问题在else这里。send立即完成时会有问题。

线程1 send立即完成,此时进入else,这时候你把空间归还了。 因为你认为iocp不会触发

线程2 现在另外一个地方开始send,拿到你刚才那个buf。

io线程 iocp被触发: 把你刚才的buf再归还一次

线程2 现在你拿到的buf,但是标志完全是错误的,可能再被另外地方使用。最后就是不知道发了什么东西到sendto上面。也有可能2个sendto用一个overlapped结构。








shenyi0106 2012-03-25
  • 打赏
  • 举报
回复
此问题已解决,最终原因是没有处理1234和998这两个GetLastError消息导致的。
非常谢谢各位的热情参与和讨论,我将会在周一下班前平均散掉这300分,以答谢各位的帮忙
shenyi0106 2012-03-25
  • 打赏
  • 举报
回复
[Quote=引用 65 楼 kevin_qing 的回复:]
//直接发送成功
else
{
CSockData::GetInstance()->PushIn(hSock, PerIoData);
}
这里有bug

直接发送成功,iocp仍然会有回调的,造成pushin 2次 。
也就是说你会把正在用的缓冲标记为空闲 ,不可预料会发生什么事情。
[/Quote]
这个就更不对了,我不知道你在什么OS上测试的,我在XP和2003上都测试过,只要是成功WSASend(To),并且返回的不是SOCKET_ERROR,那么就不会存在完成通知,而且一开始我还不相信,我还测试了两三遍
加载更多回复(56)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧