基于tcp/ip的"长连接"会发生"粘包"吗?

by_封爱 版主 2017-03-20 02:21:17
看这个帖子 http://bbs.csdn.net/topics/392138884中2#的回复.

引用
数据“粘包”很正常,因为tcp是基于流的。
并不是客户端发a,b,c,服务端就收到a,b,c
有可能是ab,c,有可能是 a,bc或者abc


是这样吗?为什么我没有遇到过?

我做过这种服务端 跟模块通讯 跟手机通讯 都做过. 但是从来没遇到过这种问题..我记得是不是UDP有这样的事而tcp没有?

所以现在是怎样?
...全文
821 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
青蛙工作室 2017-03-22
  • 打赏
  • 举报
回复
粘包分包多正常啊,自己处理一下就好。
love_jarkic 2017-03-21
  • 打赏
  • 举报
回复
网络差的情况下是会有粘包的
Poopaye 2017-03-20
  • 打赏
  • 举报
回复
最后出来Length应该是2,也就是一次Receive接收了所有数据
Poopaye 2017-03-20
  • 打赏
  • 举报
回复
引用 6 楼 diaodiaop 的回复:
服务器的beginrev是会执行2次的而不是合并....哪怕发送的只是0x01跟0x02 这个我测试过的
肯定是你没等2次send结束 自己试试吧:
const int PORT = 5566;

//Listen
Socket recver = new Socket(SocketType.Stream, ProtocolType.Tcp);
Socket comm = null;
recver.Bind(new IPEndPoint(IPAddress.Any, PORT));
recver.Listen(32);
recver.BeginAccept(ar => comm = recver.EndAccept(ar), null);

//Send
Socket sender = new Socket(SocketType.Stream, ProtocolType.Tcp);
sender.Connect(IPAddress.Loopback, PORT);
sender.Send(new byte[] { 1 });
sender.Send(new byte[] { 2 });
sender.Close();

//Receive
Thread.Sleep(1000); //Wait for connection
comm.BeginReceive(new byte[10], 0, 10, SocketFlags.None, ar => Console.WriteLine("Length: " + comm.EndReceive(ar)), null);
Thread.Sleep(1000); //Wait for receive
comm.Close();
recver.Close();
laymat 2017-03-20
  • 打赏
  • 举报
回复
粘包涉及到的其实就是一个协议问题,tcp是可靠协议出现丢包的情况只会是网络问题,而如果采用约定的协议没有读取到完整的数据则不会对此次操作做处理也不会回复这个处理状态,这样就可以再根据超时规则重试即可。
xuzuning 2017-03-20
  • 打赏
  • 举报
回复
其实纠结这个 分包、粘包 的问题是没有意义的,只要总量不少就行了 既然 有可能是ab,c,有可能是 a,bc或者abc 并且你也不能确定究竟是在哪个环节,连续的数据包被重新组装了 那么就没有必要去管他 而况你在接收时,还和数据缓冲区的大小有关
crystal_lz 2017-03-20
  • 打赏
  • 举报
回复
至于分包 就不说了 你直接Send(来个几M)的数据试试? 看看 你那边是不是能一次性就立马收到几M
crystal_lz 2017-03-20
  • 打赏
  • 举报
回复
引用 10 楼 diaodiaop 的回复:
[quote=引用 7 楼 xdashewan 的回复:]
....


奇怪 为什么我测试的时候 从来没出现过这情况呢? 都是正常的"一组数据" 没有分开过接收..[/quote]
通常情况下这种情况比较少
无论tcp还是udp到底层你发送的数据都将被封装成一个数据包系统帮你加上ip包头 总的说来数据还是一坨一坨的出去的 udp是数据包的形式 所以一个数据那么就是一个包 但是tcp是 即使是一个一个的把数据包发送到对方机器上 系统会把一个一个的包刷如对方的缓存的队列里面来给程序 而不会去给你区分每个数据包多大多大什么的

明显 上面我发送的是两个数据包 但是我的server 由于某种原因并不能很即使的recvice到 但是这两个数据已经在我接收的队列里面了 通过Socket.Recvice来从队列里面读出数据来 明显我一次把两个数据包都读出来了
Forty2 2017-03-20
  • 打赏
  • 举报
回复
IPV4至少必须支持68字节的TCP packet,最大可以支持到64K。但网络上的路由点,可能有不同的设置,或许是1452,1500等。 如果你的数据大于2000字节,传送远一些(多跳一个hops),那么你看到‘分包’的可能性就比较高了。 如果用了wifi,
引用 11 楼 diaodiaop 的回复:
[quote=引用 9 楼 Forty2 的回复:] ...
好吧 我测试了 是分1次收到.. 可能我的例子跟问题不太匹配 也就是 客户端发送100个字节 服务端是分2次收到 第一次22个 第二次78个 这种 我没有遇到过... 但是也可能是我传输的数据太小? 因为协议总长度的话 也不过超过200个 跟这个有关系?[/quote] IPV4至少必须支持68字节的packet,最大可以支持到64K。但网络上的路由点,可能有不同的设置,MTU或许是1452,1500等。 如果你的数据大于3000字节,传送远一些(多跳几个hops),那么你看到‘分包’的可能性就比较高了。 如果用了wifi,MTU(Maximum Transmission Unit)是2304,就比较容易观察到‘分包’了。
wanghui0380 2017-03-20
  • 打赏
  • 举报
回复
tcp一直都有这样的问题。 ps:其实udp和tcp的处理过程是一模一样的,所以你说udp会,那么tcp也会。他们都在system.net里,他们内部都采用 public Socket Client { get; set; } 来处理,处理机制完全一样 当然这种测试其实靠运气,socket处理发送和接收内部机制还是很复杂的。不然你认为原帖里为啥会有“加大发送时间”,而博客园里为啥经常出现“while(true)”,"sleep"这样的玩意。原因就是因为他们总想保证“一条一发,一条一收”这样的逻辑。 “加大发送时间”---他想等着上一次的发完了在发下一次,可惜网络状况谁也不知道,谁也不知道上一次什么时候发送完(所以博客园里也有另外的搞法,发送入队列,tcp自己去队列里取,发送一个取下一个) “while(true)”,"sleep"这样搞滴,是发现一次没收完整,好吧,我先睡会,等收完整了在处理。但是你要睡多长时间,他会“碰巧”刚刚好就是一整个包(刚刚好就是从头到尾都是他的,期间没有混合别地),为了处理这样滴,博客园又发明了服务器那边也睡会的做法,两边都睡一会,就能保证发的时候是一条,接的时候也是一条(大部分时间两边都在睡,就等这那一条过去)
songbing774933 2017-03-20
  • 打赏
  • 举报
回复
UDP反倒还没有这个问题。 因为UDP是你发送一个包,它就发送一个包。
引用 11 楼 diaodiaop 的回复:
[quote=引用 9 楼 Forty2 的回复:] ...
好吧 我测试了 是分1次收到.. 可能我的例子跟问题不太匹配 也就是 客户端发送100个字节 服务端是分2次收到 第一次22个 第二次78个 这种 我没有遇到过... 但是也可能是我传输的数据太小? 因为协议总长度的话 也不过超过200个 跟这个有关系?[/quote] 发送连续的小包就可能粘包 一次发送一个很大的包,就可能分包
by_封爱 版主 2017-03-20
  • 打赏
  • 举报
回复
引用 9 楼 Forty2 的回复:
...
好吧 我测试了 是分1次收到.. 可能我的例子跟问题不太匹配 也就是 客户端发送100个字节 服务端是分2次收到 第一次22个 第二次78个 这种 我没有遇到过... 但是也可能是我传输的数据太小? 因为协议总长度的话 也不过超过200个 跟这个有关系?
by_封爱 版主 2017-03-20
  • 打赏
  • 举报
回复
引用 7 楼 xdashewan 的回复:
....
奇怪 为什么我测试的时候 从来没出现过这情况呢? 都是正常的"一组数据" 没有分开过接收..
Forty2 2017-03-20
  • 打赏
  • 举报
回复
引用 楼主 diaodiaop 的回复:
... 我做过这种服务端 跟模块通讯 跟手机通讯 都做过. 但是从来没遇到过这种问题..我记得是不是UDP有这样的事而tcp没有? 所以现在是怎样?
UDP是基于消息的,因此UDP要么没有收到,要么整个消息收到,不会出现一个消息分几次收到,或一次收到几个消息的现象。 而TCP是基于流的,TCP从来就没有保证过分包。 diaodiaop朋友在6楼的代码,如果朋友们实测,99%以上将一次性收到。 原因是TCP包有数据头的额外开销。假设你发一个byte,加上额外开销,网络上要走有41个字节。因此,默认的Nagle's algorithm将尝试优化,把几个小的数据合并发送。Windows默认开启Nagle。 即使你把Nagle关掉,由于网络的路由,你的数据可能经由不同的路径到达目的。还是可能出现收到a,ab,abc'粘在一起’的现象。另外,还有可能收到a1, a2, a3b, 等数据被拆分的现象。
拜一刀 2017-03-20
  • 打赏
  • 举报
回复
Nagle算法是以他的发明人John Nagle的名字命名的,它用于自动连接许多的小缓冲器消息;这一过程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效率。 http://baike.baidu.com/link?url=-67Yn2sw6S0F8adbDqpLQF26xCYeIXzYsxkJ7wvzVqXShgMMT0wp4UVHHcFPJ6osNX0vjoaoRnSd9eEfVGn4_FdhGVW8HbKk7lB5q3SkKQu 看来我的印象来自这里....我也不知道是不是各种环境都会有这种情况发生
xdashewan 2017-03-20
  • 打赏
  • 举报
回复
引用 6 楼 diaodiaop 的回复:
服务器的beginrev是会执行2次的而不是合并....哪怕发送的只是0x01跟0x02 这个我测试过的
发送,分2次,1次32byte 接受1次64byte
by_封爱 版主 2017-03-20
  • 打赏
  • 举报
回复
引用 2 楼 xdashewan 的回复:
有,你服务器把断点放在beginreceive上,客户端连发两条,服务器再继续执行,你接收的buff足够大就会两条一次性收到
你这个测试过吗? 客户端使用下面

            TcpClient c = new TcpClient();
            c.Connect(IPAddress.Parse(ip), 8421);
            c.Client.Send(ds1);
            c.Client.Send(ds2);
            c.Client.Shutdown(SocketShutdown.Both);
            c.Client.Close();
            c.Close();
服务器的beginrev是会执行2次的而不是合并....哪怕发送的只是0x01跟0x02 这个我测试过的
拜一刀 2017-03-20
  • 打赏
  • 举报
回复
竟然不是引用赵四老师的回复(人多病少财富的坑啊)←_← 我记得为了有效利用网络资源,有可能会把短时间内连续发送的包合并到一起?反正DTU是有过两包一起的时候
xian_wwq 2017-03-20
  • 打赏
  • 举报
回复
使用局域网或者网络条件好, 或许不明显 但使用公网或者网络条件较差,这种情况非常明显
xdashewan 2017-03-20
  • 打赏
  • 举报
回复
其实你想想,就有因为有这种情况发生,才会有一问一答的ack包不是吗?
加载更多回复(2)

110,551

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

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