IOCP 多线程 接收 问题

songtao_01 2009-05-13 05:11:23
问个问题,用iocp,多线程模式下接受的时候会乱序吗?就比如一个连接先后发了两次数据:1:abcdef 2:hijklm;
然后接受到的时候,会不会是abcklmdefhij呢?如果会,如何解决。如果不会,原因是什么?粘包好解决,就是这乱序。。。。另外这种乱序我在实验过程中没出现过,但是理论上可能会出现吧,我说的对吗?敬请帮助!
...全文
703 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
sorawa 2012-07-18
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 的回复:]
我想请问一下,SOCKET中RecvBuf里面它开设有多大,我听说是8192最大?
也是我们现在所做的SOCKET编程的send or recv是从操作系统里面为这个SOCKET开设的BUFFER中以送或接收数据,

是这样理解吗?
[/Quote]

不是这样理解,初始化缓冲区大小需要时间,操作缓冲区也需要时间,而8192 和 4096 权衡性能和缓冲大小都是很合适的。
woshisaoge 2012-07-09
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]

多Thread接收也不会错的

除非你同时,向一个SOCKET 发送2个Recv请求。但是如果出现这种情况你是
应该将后一个Recv排队的等待第一个Recv完成,之后在发送Recv请求
[/Quote]

对于每个客户端连接来说,需要用队列保存吗?
jy02404957 2009-08-06
  • 打赏
  • 举报
回复
多线程处理IO 有必要吗?
shenyi0106 2009-06-04
  • 打赏
  • 举报
回复
Send---->Recv 不会乱

Send Recv
Send -------> Recv 会乱,你应该加序号,这是你应用层的问题,应该你自己处理
Send Recv
mme 2009-06-04
  • 打赏
  • 举报
回复
6楼说的没错,关键就是每次只投递一个recv请求
Leo_red 2009-05-27
  • 打赏
  • 举报
回复
你再往同一个缓存里面写,就接着刚才已经写了的位置写喽,不然从缓冲区的头开始写,不就吧刚才写的东西冲掉了?
这个和粘包处理的方法一样嘛
rularys 2009-05-27
  • 打赏
  • 举报
回复
假如同一时刻只有一个 recv I/O 操作在等待,不会有乱序的问题;
假如同一时刻有一个以上的 recv I/O 操作在等待,则所有正在等待的操作在数据到达时,它们的完成顺序以它们被投递时的顺序为依。两种情况均与线程无关。

情况一:
任何一个线程在处理完成的recv I/O 操作时,在处理完成之前不投递下一个recv I/O ,这能保证同一时刻只有一个 recv I/O 操作在等待。

情况二:

可能同时投递了一个以上的recv I/O,但无论如何,这个“同时”对IOCP来说还是有先后顺序的,IOCP对投递的未完成操作是以先进先出的顺序来满足完成状态的。要避免多个完成的I/O操作返回时无法确认它们的先后顺序,那么在投递I/O时,可以在与I/O关联的数据中填写该I/O的投递顺序号,这样该I/O返回时,就可以根据该顺序号来确定它的完成时间位置。与I/O关联的数据,即 通常所说的单I/O数据。
songtao_01 2009-05-27
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 Leo_red 的回复:]
你再往同一个缓存里面写,就接着刚才已经写了的位置写喽,不然从缓冲区的头开始写,不就吧刚才写的东西冲掉了?
这个和粘包处理的方法一样嘛
[/Quote]
大佬,你又误会了,我意思说的是发过来的两个包可能穿插在一起
songtao_01 2009-05-26
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 Leo_red 的回复:]

同意,顺序由TCP协议底层保证,你按照顺序发,就肯定按照顺序收到的。
实际应用中也不会乱序,唯一的问题是你如何操作缓冲区。

你按照abcdefg来发送,收到的顺序肯定是abcdefg。你所说的一个线程收了abcd正往缓冲区里面写,另外一个线程收了efg也往里面写,就会乱。这显然…
[/Quote]
你误会我的意思了,我的意思是abcd已经写入缓存了,然后另一个线程收到了klm,然后klm往缓存写那不是就乱了
Leo_red 2009-05-26
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 hwsts2 的回复:]
如果你能保证 发送端发送数据是有序的,那么到达接受端缓冲区内,
数据都是有序的,不会乱序,这是TCP协议规定的
[/Quote]

同意,顺序由TCP协议底层保证,你按照顺序发,就肯定按照顺序收到的。
实际应用中也不会乱序,唯一的问题是你如何操作缓冲区。

你按照abcdefg来发送,收到的顺序肯定是abcdefg。你所说的一个线程收了abcd正往缓冲区里面写,另外一个线程收了efg也往里面写,就会乱。这显然是你对共享缓冲区的互斥没有控制啊,对这个缓冲区的操作做好互斥,肯定没有你担心的问题。
allix123 2009-05-24
  • 打赏
  • 举报
回复

我想请问一下,SOCKET中RecvBuf里面它开设有多大,我听说是8192最大?
也是我们现在所做的SOCKET编程的send or recv是从操作系统里面为这个SOCKET开设的BUFFER中以送或接收数据,

是这样理解吗?
songtao_01 2009-05-22
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 hwsts2 的回复:]
在一个连见上同时投递多个请求的问题:
其实无论你投递多少个请求,在最下面也是顺序完成请求的,
一般情况下你会发现你的数据都会如你所愿的接受回来,不会存在多大问题

但是有一个种情况你是很难处理的,在Recv端同时投递A,B2个请求, Client端发送
1000字节数据,结果地层接受到 500字节的时候缓冲区满了,
这个时候,你第一个请求A会顺利的返回,但是接受到的字节是 500个,也就是你
还要继续发送请求C才能接,受到剩余的数…
[/Quote]

对,我也是觉得如果真出现这种情况的话,就不好处理了,加标示也没用

人呢,多讨论啊

我看游戏服务端代码里的好像没有处理什么乱序问题,难道真的不会乱?
hwsts2 2009-05-19
  • 打赏
  • 举报
回复
在一个连见上同时投递多个请求的问题:
其实无论你投递多少个请求,在最下面也是顺序完成请求的,
一般情况下你会发现你的数据都会如你所愿的接受回来,不会存在多大问题

但是有一个种情况你是很难处理的,在Recv端同时投递A,B2个请求, Client端发送
1000字节数据,结果地层接受到 500字节的时候缓冲区满了,
这个时候,你第一个请求A会顺利的返回,但是接受到的字节是 500个,也就是你
还要继续发送请求C才能接,受到剩余的数据,但是这个时候 你在发送一个请求
C的话,那么这个请求会被排在第二个请求B的后面,也就是说B会把剩余的500字
节接收回来.

还有IOCP的设计目的,是处理海量连接,而不是在单连接的数据处理效率
auly403 2009-05-19
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 songtao_01 的回复:]
引用 15 楼 scq2099yt 的回复:

不明白为什么要多个线程里接受来自同一个端口的数据,这样能给你带来什么?

可以带来效率。
[/Quote]


持怀疑态度,
除非接收的线程你要处理很多工作,比如检验,解密,之类,
那就可以带来效率
fengge8ylf 2009-05-19
  • 打赏
  • 举报
回复
我不明白的是发送的时候 是不是等上一个发送成功了 再投递 还是不必理上一个发送 我看BOOST的ASIO 好像可以连续投递发送
不敢确定
fengge8ylf 2009-05-19
  • 打赏
  • 举报
回复
投递一次请求 等这个请求完成后再投递下一个请求 即使是多线程也不会乱序
makui 2009-05-19
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 hu_zhi_de 的回复:]
是可以提高效率:因为不用线程收一个,再投下一个WSARECV();
CPU就调用一次就可以接收两次的数据了.
[/Quote]

我觉得你这个有两个方面的问题需要考虑:
1.第一次调用的WSARecv接收的的数据是否需要在第二次接收到的数据之前处理呢?
2.假如第一次你需要读取1024的数据,你实际读到你的数据可能大于这个值;那你第二次读取到的数据怎么处理?

按你想的,还不如把WSABuf设大一点减少调用的次数
lhsxsh 2009-05-19
  • 打赏
  • 举报
回复
服务线程和CUP的多少有关系。
这样才能使用CUP工作的更好
scq2099yt 2009-05-14
  • 打赏
  • 举报
回复
不明白为什么要多个线程里接受来自同一个端口的数据,这样能给你带来什么?
xghuzd 2009-05-14
  • 打赏
  • 举报
回复
是可以提高效率:因为不用线程收一个,再投下一个WSARECV();
CPU就调用一次就可以接收两次的数据了.
加载更多回复(15)
1、本课程是一个干货课程,主要讲解如何封装服务器底层,使用Tcp/ip长连接,IDE使用vs2019 c++开发以及使用c++11的一些标准,跨平台windows和linux,服务器性能高效,单服务器压力测试上万无压力,服务器框架是经历过上线产品的验证,框架简单明了,不熟悉底层封装的人,半个小时就能完全掌握服务器框架上手写业务逻辑。2、本课程是一个底层服务器框架教程,主要是教会学员在windows或linux下如何封装一个高效的,避免踩坑的商业级框架,服务器底层使用初始化即开辟内存的技术,使用内存池,服务器运行期间内存不会溢出,非常稳定,同时服务器使用自定义哈希hashContainer,在处理新的连接,新的数据,新的封包,以及解包,发包,粘包的过程,哈希容器性能非常高效,增、删、查、改永远不会随着连接人数的上升而降低性能,增、删、查、改的复杂度永远都是恒定的O(1)。3、服务器底层封装没有使用任何第三方网络库以及任何第三方插件,自由度非常的高,出了任何BUG,你都有办法去修改,查找问题也非常方便,在windows下使用iocp,linux下使用epoll.4、讲解c++纯客户端,主要用于服务器之间通信,也就是说你想搭建多层结构的服务器,服务器与服务器之间使用socket通信。还可以使用c++客户端做压力测试,开辟多线程连接服务器,教程提供了压力测试,学员可以自己做压力测试服务器性能。5、赠送ue4和unity3d通信底层框架以及多人交互demo,登录,注册,玩家离开,同步主要是教会学员服务器与客户端如何交互。6、赠送c++连接mysql数据库框架demo,登录,注册,玩家离开数据持久化.7、服务器教程使用自定义通信协议,同时也支持protobuf,选择权在开发者自己手里,想用什么协议都可以,自由度高。8、服务器教程使用手动敲代码逐句讲解的方式开展教学课程。非喜勿喷,谢谢大家。9、服务器教程提供源码,大家可以在平台提供的地址下载或者联系我,服务器使用c++11部分标准,std::thread,条件变量,线程锁,智能指针等,需要学员具备一定c++知识,购买前请慎重考虑。
完成端口通讯服务器(IOCP Socket Server)设计 (六)功能强大的IOCP Socket Servre模块例程源码 Copyright © 2009 代码客(卢益贵)版权所有 QQ:48092788 源码博客:http://blog.csdn.net/guestcode 一、声明 版权声明: 1、通讯模块代码版权归作者所有; 2、未经许可不得全部或部分用于任何项目开发; 3、未经许可不得部分修改后再利用源码。 免责声明: 1、 由于设计缺陷或其它Bug造成的后果,作者不承担责任; 2、未经许可的使用作者不提供任何技术支持服务。 权利和义务: 1、任何获得源码并发现Bug的个人或单位均有义务向作者反映; 2、作者保留追究侵权者法律责任的权利。 二、开发背景 部分代码由前项目分离而来,尚未有应用考验,但对于初学者学习和进阶有很大帮助。性能上尚未有定论,但应该不会令你失望。 三、功能说明 1、可以关闭Socket的Buffer; 2、可以关闭MTU(不等待MTU满才发送); 3、可以多IP或多端口监听; 4、可以重用socket(主动关闭除外); 5、可以0缓冲接收(Socket的Buffe = 0时,避免过多的锁定内存页); 6、可以0缓冲连接(客户端仅连接,不一定立即发数据); 7、可以条件编译: a、是否使用内核Singly-linked lists; b、是否使用处理线程(工作线程和处理线程分开); c、是否使用内核锁来同步链表。 8、可以实现集群服务器模式的通讯(有客户端socket); 9、可以单独设置每个连接的Data项来实现连接和Usernfo的关联; 10、每个线程有OnBegin和OnEnd,用于设置线程独立的对象(数据库会话对象); 11、可以提供详细的运行情况,便于了解IOCP下的机制,以及进行调试分析; 12、可以发起巨量连接和数据(需要硬件配置来支持)。
//一个简单的使用例子 //连接远程服务器成功 或 接收到一个远程连接时,本函数将会被ioc.dll回调.在本函数中,应该向客户端列表中添加节点,记得加锁 // //2.s :套接字句柄,标志着一个新的连接 //3.u_addr:对端的IP地址,网络字节序 //4.u_port:对端的端口号,网络字节序 //5.flag :如果是本地连接上了一个远程服务器,flag值为0,或者,是接收到一个远程客户端的连接,这时,flag值为1 //6.返回值:返回一个自定义的值,这个值将在其他回调函数中作为参数传递(注意:就如套接字句柄一样,这个值也最 //好能标志一个连接,比如,客户端列表的某个节点的指针,假设用STL中的MAP来维护客户端列表,那么用KEY作为返回值也不错。) long _stdcall ioc_call_connect(HIOC hIoc,HINT s,long u_addr,long u_port,long flag) { long res=0; printf("socket(%d) connected\n",s); return res; } //断开与远程服务器的连接 或 远程客户端断开,本函数将会被ioc.dll回调.在本函数中,你可以删除客户节点,记得加锁 //s :套接字句柄,标志着哪个连接断开了(在本回调函数返回之前,套接字句柄s不可能被重新利用,所以,用s作为关键字构建客户端列表也是没有问题的) //res:ioc_call_connect回调函数返回的那个值,如果它是客户端列表的某个节点的话,那么可以删除了。 void _stdcall ioc_call_disconnect(HIOC hIoc,HINT s,long res,long flag) { printf("socket(%d) disconnected\n",s); } //当ioc内部一收到数据就会回调本函数,所谓数据有可能是多个数据包,也有可能是一个不完整的数据包 //hIoc,s,flag都不再多做解释 //res :ioc_call_oprate_dat和ioc_call_disconnect一定是被线性回调的,不可能存在同时执行的情况,所以,res如果指向某个节点的话,在本函数中可以不加锁地尽情访问,在本函数返回之前,res不会被释放掉 //hArg:数据调度句柄 //data:数据 //len :数据大小 //返回值:返回剩余未处理完的字节数 long _stdcall ioc_call_oprate_dat(HIOC hIoc,HARG hArg,HINT s,long res,long flag,char *data,long len) { //假如数据包格式是这样: //struct DATAPACK //{ // long size; long cmd; ...... //}; //如果是这样的话,那么典型的处理方法如下: char *p=data; long size_res=len;//收到数据的总字节数 long size_per;//其中某一个数据包的字节数 while(1) { if(size_res<4) return size_res;// size_per=*(long*)(p+0x00);//取数据包的实际长度 if(size_per<0 || size_per>某个最大值) { ::iocCommon_CloseSocket(hIoc,s); return 0; } if(size_resiocCommon_DispatchMessage(hIoc,hArg,msg);//hArg就只有这么一个作用 } p+=size_per; size_res-=size_per; } } //阻塞数据处理线程回调函数 void _stdcall ioc_call_oprate_msg(HIOC hIoc,void *msg) { //处理数据包 ::free(msg);//根据谁分配谁释放的原则,释放msg } int main() { ::iocCommon_Startup(); long addr_con=(long)ioc_call_connect; long addr_dis=(long)ioc_call_disconnect; long addr_dat=(long)ioc_call_oprate_dat; long addr_msg=(long)ioc_call_oprate_msg;//这个参数是可选的,可以不要专门的阻塞数据处理线程 HIOC hIoc=::iocCommon_Create(3072,128,addr_con,0,addr_dis,0,addr_dat,0,addr_msg,0);//创建IOC ::iocCommon_SetOprateThread(hIoc);//增加一个工作线程 ::iocCommon_SetOprateThread(hIoc);//增加一个工作线程 //启动服务器,内部循环调用阻塞的accept函数,ioc不考虑客户端连接服务器有多困难,而只考虑如何高效地进行数据传输 //可以再创建几个线程,多调用几个iocServer_Start,各个iocServer_Start绑定不同端口也可以 ::iocServer_Start(hIoc,NULL,6800); ::iocCommon_Cleanup(); return 0; }

18,356

社区成员

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

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