为什么这种情况下会recv会出错?

mbctb 2015-07-22 09:00:56
我用的是TCP协议,在客户端和服务器之间传输数据时,我是这样的:每块数据都有一个数据头,数据头中定义了该数据的长度。然后接着才是真正的数据块。

然后在发送的时候,我是这样的:发送端用两次send函数来发送,一次发送数据头,一次发送真正的数据。接收端也用两次recv来接收。一次接收数据头,数据头收到以后,根据其中给出的长度,再进行第二次recv,接收的长度就是数据头中给出的长度。

可是我发现:这样子的话,会出错。第二次recv收不到数据。

如果发送的时候,把两块数据放在一起,用一次send发送。发现接收端两次recv就能正确收到。或者发送的时候虽分两次send发送。但接收的时候,只用一次recv(当然我预先知道总的长度),也可以把全部数据(包括数据头和数据体)全部收到。

这是怎么回事呢?不管接收端是阻塞方式还是非阻塞方式,好象均如此。
...全文
384 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
xian_wwq 2015-07-28
  • 打赏
  • 举报
回复
1。tcp是基于流的,所以必须在业务层自行处理“粘包” 发一个包,接收一次可能得到一个完整的包,也可能收不全; 同理,发两个包,也可能一次就收全了。 2。调试通讯程序时,打断点Debug的方式不可行,建议 使用借助文本日志进行调试。
zilaishuichina 2015-07-27
  • 打赏
  • 举报
回复
send 123 可能recv的情况有 recv 1 recv 2 recv 3 或者 recv 12 recv 3 或者 recv 1 recv 23 或者 recv 123 !!!recv并不一定严格按照你期望的长度来接收数据
  • 打赏
  • 举报
回复
引用 9 楼 mbctb 的回复:
[quote=引用 8 楼 dcmilan 的回复:] [quote=引用 7 楼 mbctb 的回复:] [quote=引用 6 楼 xiaoxiaoyu85 的回复:] 去看看异步socket和select IO模型后,就明白了。 你recv的时候,肯定是随意写了个比你的(数据头 + 数据体 )还要大的数字,一次性把数据头和数据体都从接收缓冲区‘拷贝’出来了,第二次recv,就没有数据了。
不是的,我每次recv的时候,都是recv准确的尺寸。 目前这个问题虽然没有找到原因,但我找到了解决办法,就是在发送的时候,一次性的发,把数据头和数据体放在一起,一次性用send发送。好象这样就行了。接收端两次recv,也没有问题。 接收端非得两次recv不可。因为它第一次只能读出数据头,取出数据体长度,第二次才能读这个数据体长度。 反正这样就行了,谢谢大家,以后出问题再研究。看来网络编程真的有不少需要注意的地方。[/quote] 你为什么要接收准确大小的数据呢?你肯定要按最大接收字节数来啊。 你只要把规约设计好了,就不会出现这种不匹配的问题。[/quote] 你是说要按最大字节数来接收? 我的服务器端是非阻塞模式,客户端,在登录时是阻塞模式,登录以后,设为非阻塞模式。 你的意思是:每一次recv的时候,按缓冲区的最大尺寸来接收。至于能收多少,不管管它。然后再分析第一个数据包的头部在哪 里,数据体在哪里。第二个数据包的头部在哪里,数据体在哪里。是吗? [/quote] 对。你要有一个通信规约来说明报文的格式。 我写段伪代码 void OnReceive(CBuffer & buffer) { // 检查报文头,假设一帧报文不超过255字节,第一个字节代表报文长度,最后一个字节代表校验和 uint8 len =buffer.at(0); int i = 0; uint8 uSums = 0; for(i = 1 ; i< len; ++i) { uSums += buffer.at(i); } if(uSums != buffer.at(len-1)) { buffer.clear(); return; } // 处理合格报文 // processing.... buffer.remove(len); }
weiaichun 2015-07-23
  • 打赏
  • 举报
回复
你发的快了,产生粘包了,收包的时候自己把他拆下来
mbctb 2015-07-23
  • 打赏
  • 举报
回复
引用 8 楼 dcmilan 的回复:
[quote=引用 7 楼 mbctb 的回复:] [quote=引用 6 楼 xiaoxiaoyu85 的回复:] 去看看异步socket和select IO模型后,就明白了。 你recv的时候,肯定是随意写了个比你的(数据头 + 数据体 )还要大的数字,一次性把数据头和数据体都从接收缓冲区‘拷贝’出来了,第二次recv,就没有数据了。
不是的,我每次recv的时候,都是recv准确的尺寸。 目前这个问题虽然没有找到原因,但我找到了解决办法,就是在发送的时候,一次性的发,把数据头和数据体放在一起,一次性用send发送。好象这样就行了。接收端两次recv,也没有问题。 接收端非得两次recv不可。因为它第一次只能读出数据头,取出数据体长度,第二次才能读这个数据体长度。 反正这样就行了,谢谢大家,以后出问题再研究。看来网络编程真的有不少需要注意的地方。[/quote] 你为什么要接收准确大小的数据呢?你肯定要按最大接收字节数来啊。 你只要把规约设计好了,就不会出现这种不匹配的问题。[/quote] 你是说要按最大字节数来接收? 我的服务器端是非阻塞模式,客户端,在登录时是阻塞模式,登录以后,设为非阻塞模式。 你的意思是:每一次recv的时候,按缓冲区的最大尺寸来接收。至于能收多少,不管管它。然后再分析第一个数据包的头部在哪 里,数据体在哪里。第二个数据包的头部在哪里,数据体在哪里。是吗?
  • 打赏
  • 举报
回复
引用 7 楼 mbctb 的回复:
[quote=引用 6 楼 xiaoxiaoyu85 的回复:] 去看看异步socket和select IO模型后,就明白了。 你recv的时候,肯定是随意写了个比你的(数据头 + 数据体 )还要大的数字,一次性把数据头和数据体都从接收缓冲区‘拷贝’出来了,第二次recv,就没有数据了。
不是的,我每次recv的时候,都是recv准确的尺寸。 目前这个问题虽然没有找到原因,但我找到了解决办法,就是在发送的时候,一次性的发,把数据头和数据体放在一起,一次性用send发送。好象这样就行了。接收端两次recv,也没有问题。 接收端非得两次recv不可。因为它第一次只能读出数据头,取出数据体长度,第二次才能读这个数据体长度。 反正这样就行了,谢谢大家,以后出问题再研究。看来网络编程真的有不少需要注意的地方。[/quote] 你为什么要接收准确大小的数据呢?你肯定要按最大接收字节数来啊。 你只要把规约设计好了,就不会出现这种不匹配的问题。
mbctb 2015-07-23
  • 打赏
  • 举报
回复
引用 6 楼 xiaoxiaoyu85 的回复:
去看看异步socket和select IO模型后,就明白了。 你recv的时候,肯定是随意写了个比你的(数据头 + 数据体 )还要大的数字,一次性把数据头和数据体都从接收缓冲区‘拷贝’出来了,第二次recv,就没有数据了。
不是的,我每次recv的时候,都是recv准确的尺寸。 目前这个问题虽然没有找到原因,但我找到了解决办法,就是在发送的时候,一次性的发,把数据头和数据体放在一起,一次性用send发送。好象这样就行了。接收端两次recv,也没有问题。 接收端非得两次recv不可。因为它第一次只能读出数据头,取出数据体长度,第二次才能读这个数据体长度。 反正这样就行了,谢谢大家,以后出问题再研究。看来网络编程真的有不少需要注意的地方。
yzm365487848 2015-07-23
  • 打赏
  • 举报
回复
。。。。。。。。。。。。。。。。 send 俩次 并不一定非要recv俩次吧!!!!!! 就算是send俩次tcp 底层还不是一次性把数据发出去了。具体recv几次是要看你数据量的大小。。 recv的时候判断收到包的长度,如果长度不够 就继续 while (nRecvLen < nTotalLen) ::recv(); 。。。
  • 打赏
  • 举报
回复
正常都是不断的接收,把数据放到一个容器里 然后检查报文头的数据长度,把一帧完整的数据从容器取出 处理报文 往复循环 如果碰到错误报文,还要丢弃 通信这块还是要做好配合的 比如发送的节奏,还要server端处理数据的速度,不能阻塞太久
Magic丶旭 2015-07-22
  • 打赏
  • 举报
回复
把你的代码发给我看看,说不定我给你解决了呢?
96掌门师兄 2015-07-22
  • 打赏
  • 举报
回复
去看看异步socket和select IO模型后,就明白了。 你recv的时候,肯定是随意写了个比你的(数据头 + 数据体 )还要大的数字,一次性把数据头和数据体都从接收缓冲区‘拷贝’出来了,第二次recv,就没有数据了。
yaozhiyong110 2015-07-22
  • 打赏
  • 举报
回复
不管你是一次发送还是2次发送 你都应该判断send的返回值 那才代表你真正保存到缓冲区的数据是多少 像你说的这种情况 你分2次send 也许第二次send已经失败了 所以你不管几次recv都收不完数据(因为你第二次的数据根本就没发送成功) recv同理 ps: send返回失败 可以根据getlasterror判断错误 一般都是发送太快 你就需要中间sleep下
么特里亚 2015-07-22
  • 打赏
  • 举报
回复
发完第一次你加上sleep试试
赵4老师 2015-07-22
  • 打赏
  • 举报
回复
不知道有多少前人掉在TCP Socket send(人多)send(病少)send(财富) recv(人多病)recv(少财富) 陷阱里面啊! http://bbs.csdn.net/topics/380167545

18,356

社区成员

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

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