关于多线程使用同一SOCKET句柄调用WSASend各发送2个BUFFER时, 接收端乱包问题

dingyong2003 2010-12-13 03:46:46
前提: 客户端N线程T1.T2....TN同时利用WSASend各发送2块数据(消息头+真实数据), 服务端IOCP接收数据(服务端每处理完一个接收后, 才调用WSARecv发起接收请求).

问题: 服务器有乱包现象, 使用抓包工具也发现服务器收到的也不是预想中的(消息头+数据...消息头+数据....), 而是有交叉现象, 就是某个PKT的数据里包含了其他消息头, 从而导致整个服务器的接收工作乱套!

提问: 1. 是不是必须在WSASend前后必须加锁?

2. WSASend函数底层对于处理同时发多BUFFER是如何处理的?
...全文
598 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
dingyong2003 2010-12-15
  • 打赏
  • 举报
回复
非常感谢啊!我以前还真没留意最后一段话!哎···失败!以后后来人警惕!!!
fantiyu 2010-12-15
  • 打赏
  • 举报
回复
btw, 今天又无聊翻了下这个问题
MSDN的解释:
WSASend should not be called on the same socket simultaneously from different threads, because it can result in an unpredictable buffer order.


这个说明了微软已经强烈建议不要多线程对同一个socket进行wsasend投递...
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
很感谢fantiyu兄! 看来也没其他人来了, 这100分大部分归你哈! 其他的给捧场的兄弟!
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 dingyong2003 的回复:]
我的理解是这样的, SOCKET即为一个文件, 在下层, 肯定有个FCB, 这样的话, 我觉得不需要你说的"加锁,加锁就会占用内核对象"吧? 请指正!


引用 17 楼 fantiyu 的回复:

引用 15 楼 dingyong2003 的回复:
如果用Send的话肯定是有这样的问题的, 但我现在是调用的WSASend, 这个函数可以一下发送小于等于16个BUFFER, 我想的话,……
[/Quote]
从底层来说,同步行为最终会映射到spinlock或event上, 这两种行为都需要进入ring0
spinlock通常是在线程互锁初期, 它会在当前线程循环一段时间来尝试获取资源, 直到成功, 代码上类似while(1) {获取}
如果spin一段时间没拿到, 那么就会进入event阶段, 线程会被切换context, 丢出时间片来给其它线程以执行机会, 代码上类似SwitchToThread然后由线程管理器来判断event
但这两种不管怎样都是需要进入ring0的
(以上我指的是多cpu核心模式,单cpu下会直接创建event并切换context)
所以, 不管怎样, 只要有同步行为, 最终都是会进入ring0模式并浪费资源的
我不认为系统会在每一个socket上增加这种冗余的同步

当然, 我也从来没像楼主这么用过WSASend...
至于究竟系统有没有帮你同步, 写个简单程序试验下就行了
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
我的理解是这样的, SOCKET即为一个文件, 在下层, 肯定有个FCB, 这样的话, 我觉得不需要你说的"加锁,加锁就会占用内核对象"吧? 请指正!

[Quote=引用 17 楼 fantiyu 的回复:]

引用 15 楼 dingyong2003 的回复:
如果用Send的话肯定是有这样的问题的, 但我现在是调用的WSASend, 这个函数可以一下发送小于等于16个BUFFER, 我想的话, 它在下层应该是同步了的啊!


引用 14 楼 fantiyu 的回复:

如果你send端不是IOCP,不带overlapped
那么请问你:你是一次send投递的包头+包体, 还是send了……
[/Quote]
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 dingyong2003 的回复:]
如果用Send的话肯定是有这样的问题的, 但我现在是调用的WSASend, 这个函数可以一下发送小于等于16个BUFFER, 我想的话, 它在下层应该是同步了的啊!


引用 14 楼 fantiyu 的回复:

如果你send端不是IOCP,不带overlapped
那么请问你:你是一次send投递的包头+包体, 还是send了2次?

线程A:
Send 包头
Send 包……
[/Quote]
另外, 系统凭什么帮你同步呢?
要知道,同步就需要加锁,加锁就会占用内核对象
难道系统自动给每个socket自动创建一个用于同步的核心对象? 疯子才会这么干
既然有使用规范, 对于不正确的使用根本不需要系统多干涉, 你程序出问题了自然就会去找问题所在
相反, 系统如果强制给每个socket增加一个同步对象, 那浪费的资源谁买单呢?
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 dingyong2003 的回复:]
如果用Send的话肯定是有这样的问题的, 但我现在是调用的WSASend, 这个函数可以一下发送小于等于16个BUFFER, 我想的话, 它在下层应该是同步了的啊!


引用 14 楼 fantiyu 的回复:

如果你send端不是IOCP,不带overlapped
那么请问你:你是一次send投递的包头+包体, 还是send了2次?

线程A:
Send 包头
Send 包……
[/Quote]

猜测它是否同步,我想是没用的
就规范来说,WSASend就不应该多次投递
还是我前面说的, 应对同一个socket只保持一个投递, winsock32它就不是像你这样用的

dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
如果用Send的话肯定是有这样的问题的, 但我现在是调用的WSASend, 这个函数可以一下发送小于等于16个BUFFER, 我想的话, 它在下层应该是同步了的啊!

[Quote=引用 14 楼 fantiyu 的回复:]

如果你send端不是IOCP,不带overlapped
那么请问你:你是一次send投递的包头+包体, 还是send了2次?

线程A:
Send 包头
Send 包体

线程B:
Send 包头
Send 包体

线程C:
Send 包头
Send 包体

如果是这种架构,不乱序才奇怪了
[/Quote]
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
如果你send端不是IOCP,不带overlapped
那么请问你:你是一次send投递的包头+包体, 还是send了2次?

线程A:
Send 包头
Send 包体

线程B:
Send 包头
Send 包体

线程C:
Send 包头
Send 包体

如果是这种架构,不乱序才奇怪了
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 fantiyu 的回复:]

引用 8 楼 dingyong2003 的回复:
引用 7 楼 fantiyu 的回复:

IOCP架构的精髓在于,对于同一个socket,应同时只存在一个Send和一个Recv而不是多个
如果对于同一个socket同时存在多个send投递,那么必然会存在乱序问题,所以你这个架构本身就是错误的

应对socket实现一个队列, 有数据需要发送时,先判断是否已存在send投递, 如果已……
[/Quote]

你说的"多个投递同时进行, 所以没办法保证不乱序", 你说是系统层没法保证吗? 其实, 我现在的测试的WSASend投递的是不带OverLapped的, MSDN上说, 这种用法不就是send吗?
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 dingyong2003 的回复:]
引用 7 楼 fantiyu 的回复:

IOCP架构的精髓在于,对于同一个socket,应同时只存在一个Send和一个Recv而不是多个
如果对于同一个socket同时存在多个send投递,那么必然会存在乱序问题,所以你这个架构本身就是错误的

应对socket实现一个队列, 有数据需要发送时,先判断是否已存在send投递, 如果已投递了send就丢入这个socket对应的队列而不是立……
[/Quote]
根源问题告诉你了啊,就是我说的,架构问题
多个投递同时进行, 所以没办法保证不乱序, 你应该在架构上控制它只存在一个投递而不是加锁
加锁是一个很浪费资源的行为
因为互锁的时候会创建event,占用内核对象
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 xumaojun 的回复:]

客户端N线程之间没同步吧,每个线程发送一个完成的数据包(数据头+数据体)后再发送下一个数据包
[/Quote]

xumaojun兄, 这种同步在WSASend下层难道没做吗?
xumaojun 2010-12-13
  • 打赏
  • 举报
回复
客户端N线程之间没同步吧,每个线程发送一个完成的数据包(数据头+数据体)后再发送下一个数据包
stjay 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 dingyong2003 的回复:]

引用 7 楼 fantiyu 的回复:

IOCP架构的精髓在于,对于同一个socket,应同时只存在一个Send和一个Recv而不是多个
如果对于同一个socket同时存在多个send投递,那么必然会存在乱序问题,所以你这个架构本身就是错误的

应对socket实现一个队列, 有数据需要发送时,先判断是否已存在send投递, 如果已投递了send就丢入这个socket对应的队列而不是……
[/Quote]

根源在于服务端对线程调度没处理好
也就是接收数据包的线程顺序T1,T2,...,TN
但处理数据的线程顺序不一定是T1,T2,...,TN

另外,怎么不将消息头+真实数据合并成一块
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 fantiyu 的回复:]

IOCP架构的精髓在于,对于同一个socket,应同时只存在一个Send和一个Recv而不是多个
如果对于同一个socket同时存在多个send投递,那么必然会存在乱序问题,所以你这个架构本身就是错误的

应对socket实现一个队列, 有数据需要发送时,先判断是否已存在send投递, 如果已投递了send就丢入这个socket对应的队列而不是立即send
当send结束时判断是否有需要……
[/Quote]

fantiyu兄是说得是很有道理, 我现在只是想知道客户端N个线程发, 服务器IOCP收, 产生问题的根源在哪里? 出于好奇, 所以想请教高人解惑!!!
fantiyu 2010-12-13
  • 打赏
  • 举报
回复
IOCP架构的精髓在于,对于同一个socket,应同时只存在一个Send和一个Recv而不是多个
如果对于同一个socket同时存在多个send投递,那么必然会存在乱序问题,所以你这个架构本身就是错误的

应对socket实现一个队列, 有数据需要发送时,先判断是否已存在send投递, 如果已投递了send就丢入这个socket对应的队列而不是立即send
当send结束时判断是否有需要投递的数据,有数据就立即继续投递
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 oyljerry 的回复:]

引用 4 楼 dingyong2003 的回复:

我测试过加个锁就没问题, 只是想不明白, 不是说WSASend是线程安全的么? 还有, 我确信每个线程都是发送了预想中大小的数据.

发送的数据区数据可能不加锁后,数据乱了,所以还是加锁来控制比较好
[/Quote]
我现在的意图就是想知道不加锁时, 哪里会让数据乱.
oyljerry 2010-12-13
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 dingyong2003 的回复:]

我测试过加个锁就没问题, 只是想不明白, 不是说WSASend是线程安全的么? 还有, 我确信每个线程都是发送了预想中大小的数据.
[/Quote]
发送的数据区数据可能不加锁后,数据乱了,所以还是加锁来控制比较好
dingyong2003 2010-12-13
  • 打赏
  • 举报
回复
我测试过加个锁就没问题, 只是想不明白, 不是说WSASend是线程安全的么? 还有, 我确信每个线程都是发送了预想中大小的数据.
fishion 2010-12-13
  • 打赏
  • 举报
回复
客户端N线程用不同的端口发送数据吧
加载更多回复(1)

18,356

社区成员

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

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