讨论!使固定长度数据包和缓冲区是否可以解决粘包和分包等等问题?????

xu_2007 2010-09-09 12:51:32
最近讨论网络数据粘包的问题太少了,本人就发一贴来讨论下!

在TCP网络传输中数据包粘包这个问题是一难题。所以请各位朋友给出一个答案:也就是说如果将缓冲区设为:1020字节的话,那么我们也把要传送的数据包也强制控制在1020字节(不够则补,有多则分包),那么还会不会出现粘包、分包等等问题?并且发送方不使用线程暂停函数暂停发送的方法!如果此方法可以解决上述问题的话,那么还有没有什么别的不好的地方!希望大家本着已测试过并有真实结论的态度给出准确答案。
...全文
367 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
xu_2007 2010-09-11
  • 打赏
  • 举报
回复
[Quote=引用 32 楼 cgabriel 的回复:]
引用 30 楼 xu_2007 的回复:

引用 29 楼 guoyichao 的回复:
引用 25 楼 xu_2007 的回复:
这位朋友,你还是没全部看明白我的意思,我的意思是说Queue<byte[]> 不止是放了一个客户端套接字接收的字节数组,而是放了多个客户端的字节数组;正因为如此才不好判断某一个客户端接收的多条数据的顺序问题,因为Queue<byte[]> 里面存的每条数据很可……
[/Quote]

定义固定长度很影响效率,如果是这样的话,这个问题就不需要讨论了!
xu_2007 2010-09-11
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 guoyichao 的回复:]
本来用Dictionary表示是想说的简单点的,从你的描述看,你的编程知识还处在入门阶段,那稍微说的详细点
假定有这么一个类:
class Peer
{
TcpClient c;
Queue<byte[]> buffer
}
及 一个List<Peer> peers;

当Accept的时候就创建一个Peer,把c设为Client,并存入peers。到此在peers里就存放了所……
[/Quote]

老兄,麻烦你看清楚内容再回贴,你说的这个方法能处理乱顺问题吗?
看清楚:
Dictionary<Socket(A), Queue<byte[]>>
Dictionary<Socket(B), Queue<byte[]>>
Dictionary<Socket(A), Queue<byte[]>>
Dictionary<Socket(B), Queue<byte[]>>
这种情况下就是你说的方法,但你给出的:
while(true)
{
for (int i = 0; i < peers.Count; ++i)
{
if (peers[i].c.Poll(0, SelectMode.SelectRead))
{
可以读写,通过异步或同步read把数据读入peers[i].buffer
}
}
}

然后数据组装线程:
while(true)
{
for (int i = 0; i < peers.Count; ++i)
{
if (peers[i].buffer.Count > 0)
{
进行数据包拆分,组合成真正需要的数据包并发送到业务处理线程
}
}
}

能分析出哪条数据在哪条数据之后吗?我都已经说了所有数据要放在公共接收字节数组集合里;如果要用到和你那么复杂的算法才能解决的话,那我上面给出的方法不就已经解决问题了吗!
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 xu_2007 的回复:]
引用 17 楼 xu_2007 的回复:
引用 8 楼 linux7985 的回复:
定义一个包结构就好了,

有了包结构,那你管他包是不是粘在一起,反正你都是能分离出来


朋友,对于你这个说法,我现在想举个例子:比如我定义了一个Queue<byte>集合,我把所有客户端套接字接收到的byte字节都放在这里面,也就是说这里面会出现A客户端套接字的BYTE数组,也会有B客户端的BYT……
[/Quote]

以上问题是针对接收数据包一个线程,处理数据包一个线程的处理方式的,对于其它方式的数据包处理方式不在此范围内~
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 xu_2007 的回复:]
引用 8 楼 linux7985 的回复:
定义一个包结构就好了,

有了包结构,那你管他包是不是粘在一起,反正你都是能分离出来


朋友,对于你这个说法,我现在想举个例子:比如我定义了一个Queue<byte>集合,我把所有客户端套接字接收到的byte字节都放在这里面,也就是说这里面会出现A客户端套接字的BYTE数组,也会有B客户端的BYTE数组,如此不断增加...;那么,按你说的只……
[/Quote]

我想我这样描述应该够清楚问题的本质原因了吧?还请SP1234朋友,dancingbit朋友, Linux7985(上善若水)朋友以及各位了解的朋友来解答此问题!
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 linux7985 的回复:]
定义一个包结构就好了,

有了包结构,那你管他包是不是粘在一起,反正你都是能分离出来
[/Quote]

朋友,对于你这个说法,我现在想举个例子:比如我定义了一个Queue<byte>集合,我把所有客户端套接字接收到的byte字节都放在这里面,也就是说这里面会出现A客户端套接字的BYTE数组,也会有B客户端的BYTE数组,如此不断增加...;那么,按你说的只要定义一个包结构就可以解决,比如包结构为:包长度+包内容,那么就像这样来发送每个包,那么当A客户端和B客户端多次发送后,并且服务器端也多次接收了并把每次接收到的数据都一条一条的放进了Queue<byte>集合中,注意,放进的顺序不可能是按客户端发送的顺序一样放的,而是来一条就放一条;那么现在问题来了,比如我要把Queue<byte>集合中A客户端发送的几条数据组合成一个完整的包时,我根本不能知道哪条数据在哪条数据后面,也就是说我即便定义了包长度,我也不知道包含了包长度的那条数据在Queue<byte>集合中的哪个位置,即便我知道包含包长度的那条数据在哪里,并且我取出来了,那么我又怎么能知道紧接着这条数据后面的那条数据放在了Queue<byet>集合的哪个位置呢?如果你说只要找到相同IP和端口的那条数据就行了,但问题是Queue<byte>集合里面现在有N条相同IP和端口的数据,主要问题就在于此?也就是说如何做到TCP协议一样,如何把包顺序组织好!
CGabriel 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 xu_2007 的回复:]

引用 29 楼 guoyichao 的回复:
引用 25 楼 xu_2007 的回复:
这位朋友,你还是没全部看明白我的意思,我的意思是说Queue<byte[]> 不止是放了一个客户端套接字接收的字节数组,而是放了多个客户端的字节数组;正因为如此才不好判断某一个客户端接收的多条数据的顺序问题,因为Queue<byte[]> 里面存的每条数据很可能是比如:第一条是A客户端的数据,第二条是B客……
[/Quote]

你的每一份数据包就应该明确地指定长度,或者明确地制定边界。

这个做法会比你在外面搞神马队列啊,神马固定长度啊,神马乱序啊简单得多。
guoyichao 2010-09-10
  • 打赏
  • 举报
回复
本来用Dictionary表示是想说的简单点的,从你的描述看,你的编程知识还处在入门阶段,那稍微说的详细点
假定有这么一个类:
class Peer
{
TcpClient c;
Queue<byte[]> buffer
}
及 一个List<Peer> peers;

当Accept的时候就创建一个Peer,把c设为Client,并存入peers。到此在peers里就存放了所有连入得客户端TcpClient及一个读写用buffer。
然后读循环:
while(true)
{
for (int i = 0; i < peers.Count; ++i)
{
if (peers[i].c.Poll(0, SelectMode.SelectRead))
{
可以读写,通过异步或同步read把数据读入peers[i].buffer
}
}
}

然后数据组装线程:
while(true)
{
for (int i = 0; i < peers.Count; ++i)
{
if (peers[i].buffer.Count > 0)
{
进行数据包拆分,组合成真正需要的数据包并发送到业务处理线程
}
}
}

要完整代码的话可以自行搜一下论坛。
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 guoyichao 的回复:]
引用 25 楼 xu_2007 的回复:
这位朋友,你还是没全部看明白我的意思,我的意思是说Queue<byte[]> 不止是放了一个客户端套接字接收的字节数组,而是放了多个客户端的字节数组;正因为如此才不好判断某一个客户端接收的多条数据的顺序问题,因为Queue<byte[]> 里面存的每条数据很可能是比如:第一条是A客户端的数据,第二条是B客户端的数据,第三条又可能是A客户端的数据;所以这样……
[/Quote]

朋友,我就是想用Dictionary<Socket, Queue<byte[]>>这个方法解决。但是,针对我提的这个问题单纯用这个方法我不说绝对,可是也非常难解决这问题,正像我上面说的就算我们以Dictionary<Socket, Queue<byte[]>>这样的集合去存每个套接字和相应的字节数组,但由于并不是每一条集合信息里面的字节数组都是一个完整的包,基本上要两条以上的集合内容才能组成一个完整的数据包,所以在这种情况下定义包头内容也是无济于事的;比如:现在的集合是这样的:Dictionary<Socket(A), Queue<byte[]>>
Dictionary<Socket(B), Queue<byte[]>>
Dictionary<Socket(A), Queue<byte[]>>
Dictionary<Socket(B), Queue<byte[]>>
那么多必要要把两条A的字节数组合在一起才能组成一个包的话,我如何知道哪条是在前哪条是在后?这只是只有两条的情况下,如果有N条呢?那不是更搞不清顺序了?
guoyichao 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 xu_2007 的回复:]
这位朋友,你还是没全部看明白我的意思,我的意思是说Queue<byte[]> 不止是放了一个客户端套接字接收的字节数组,而是放了多个客户端的字节数组;正因为如此才不好判断某一个客户端接收的多条数据的顺序问题,因为Queue<byte[]> 里面存的每条数据很可能是比如:第一条是A客户端的数据,第二条是B客户端的数据,第三条又可能是A客户端的数据;所以这样一来根本没办法重组A客户端的几条数据为一个完整客户端。

这应该属于逻辑问题,但确实属于一个比较“难”解决的问题,似乎需要TCP保持数据不乱序的算法,但本人对TCP非常不熟,所以根本无法知道其算法的实现;再者,也许TCP实现数据不乱序的算法根本就不可能在集合里面实现,而是定义了复杂的数据结构才得以实现!

本人现想到一个方法可以解决:为每个客户端定义一个可变长的字节数组变量,把每次客户端接收到的数据预先放在此字节数组中,然后待解析线程使用解析数据包的算法来解析出每个包之后再放入Queue<解析后的对象类>中,最后由服务端数据处理线程取出处理!

但这个方法看起来似乎多了一步,很可能影响效率,也不知会不会产生大量内存碎片。所以还请各位朋友讨论是否还有更好的方法!
[/Quote]

你不会每一个socket使用一个独立的Queue么?非要把所有的socket放在一起干嘛,一个简单的Dictionary<Socket, Queue<byte[]>>就搞定了,干嘛把问题想的那么复杂。
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 cgabriel 的回复:]
引用 25 楼 xu_2007 的回复:

引用 20 楼 guoyichao 的回复:
最直观的方案:
协议上定义一个数据头4个字节(int)存放数据包实际大小,每次在socket上进行读写操作时都要把数据加上数据长度做成新的数据包发送。
程序上定义一个Queue<byte[]> buffer,一个读写线程每次从socket读出来的byte[]数据不做任何处理全部enqueue进buf……
[/Quote]

朋友,我不是说TCP会乱序,我的意思是说我每个套接字接到数据后都要存入一个公用的集合对象里面,所以这样一来会乱序,所以我才提问有没有方法在这样的乱序情况下重组数据!
CGabriel 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 xu_2007 的回复:]

引用 20 楼 guoyichao 的回复:
最直观的方案:
协议上定义一个数据头4个字节(int)存放数据包实际大小,每次在socket上进行读写操作时都要把数据加上数据长度做成新的数据包发送。
程序上定义一个Queue<byte[]> buffer,一个读写线程每次从socket读出来的byte[]数据不做任何处理全部enqueue进buffer,然后还有一个包组装线程,遍历buffe……
[/Quote]

1. TCP 是不会出现乱序的,除非你自己打乱它
2. TCP 每个客户端连接之间的数据是独立的,除非你自己混淆它

在 codeproject 上随便找个例子都能够解决你的问题。
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
对了,还有前面的几位红色星星的朋友,看你们应该是这里的老前辈了,懂的也肯定要多得多,但为什么你们的回答都那么的哲学?我的贴子头部已经说明这是一讨论贴,要的就是希望各位有经验的朋友能多说说自己的观点,这样可以让非常多的初学者至少在经过此贴之后大脑中有那么个映象,以后在写此类程序时可以有所帮助!
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 guoyichao 的回复:]
最直观的方案:
协议上定义一个数据头4个字节(int)存放数据包实际大小,每次在socket上进行读写操作时都要把数据加上数据长度做成新的数据包发送。
程序上定义一个Queue<byte[]> buffer,一个读写线程每次从socket读出来的byte[]数据不做任何处理全部enqueue进buffer,然后还有一个包组装线程,遍历buffer,按照读4个字节为长度len,读len个字节顺序……
[/Quote]

这位朋友,你还是没全部看明白我的意思,我的意思是说Queue<byte[]> 不止是放了一个客户端套接字接收的字节数组,而是放了多个客户端的字节数组;正因为如此才不好判断某一个客户端接收的多条数据的顺序问题,因为Queue<byte[]> 里面存的每条数据很可能是比如:第一条是A客户端的数据,第二条是B客户端的数据,第三条又可能是A客户端的数据;所以这样一来根本没办法重组A客户端的几条数据为一个完整客户端。

这应该属于逻辑问题,但确实属于一个比较“难”解决的问题,似乎需要TCP保持数据不乱序的算法,但本人对TCP非常不熟,所以根本无法知道其算法的实现;再者,也许TCP实现数据不乱序的算法根本就不可能在集合里面实现,而是定义了复杂的数据结构才得以实现!

本人现想到一个方法可以解决:为每个客户端定义一个可变长的字节数组变量,把每次客户端接收到的数据预先放在此字节数组中,然后待解析线程使用解析数据包的算法来解析出每个包之后再放入Queue<解析后的对象类>中,最后由服务端数据处理线程取出处理!

但这个方法看起来似乎多了一步,很可能影响效率,也不知会不会产生大量内存碎片。所以还请各位朋友讨论是否还有更好的方法!
xu_2007 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 jiabiao113 的回复:]
每个接受线程 先读包头获得包长度。
然后
while(count = 包长度)
{Receive 数据}
也就是读取一个完整的自定义包,
再加入集合
[/Quote]

这位朋友似乎大概听懂了我的意思!几乎接进解决方案了!
csproj 2010-09-10
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 jiabiao113 的回复:]
每个接受线程 先读包头获得包长度。
然后
while(count = 包长度)
{Receive 数据}
也就是读取一个完整的自定义包,
再加入集合
[/Quote]
上面少打个!
while(count != 包长度)
{
Receive 数据;
}
这里要注意Receive 的数据可能包括下一个数据包的数据。
所以每次Receive 都要计算一下,当前包的剩余长度
csproj 2010-09-10
  • 打赏
  • 举报
回复
每个接受线程 先读包头获得包长度。
然后
while(count = 包长度)
{Receive 数据}
也就是读取一个完整的自定义包,
再加入集合
guoyichao 2010-09-10
  • 打赏
  • 举报
回复
技巧有点错误,不是把多读的数据重新enqueue而是需要插到队列最面,也就是说dequeue的时候需要先peek一下,如果会读多那需要重写头部的数据,.net自带的Queue没这个功能,需要自己设计一个。
guoyichao 2010-09-10
  • 打赏
  • 举报
回复
最直观的方案:
协议上定义一个数据头4个字节(int)存放数据包实际大小,每次在socket上进行读写操作时都要把数据加上数据长度做成新的数据包发送。
程序上定义一个Queue<byte[]> buffer,一个读写线程每次从socket读出来的byte[]数据不做任何处理全部enqueue进buffer,然后还有一个包组装线程,遍历buffer,按照读4个字节为长度len,读len个字节顺序处理,有个技巧是当读完len个字节后dequeue出来的byte[]还有数据时再把这些数据enqueue回buffer。
最后读完一个完整协议包后把协议包发送给业务线程处理就行了。
dancingbit 2010-09-09
  • 打赏
  • 举报
回复
你可以把tcp发送的数据序列想象为一条流动的河,而你想要你这条河在你的控制下一段一段地流动,你觉得,别人应该如何评价你这种想法?
csproj 2010-09-09
  • 打赏
  • 举报
回复
每个 socket 只有一个RemoteEndPoint 怎么可能同时接受两个套接字的数据包呢?
加载更多回复(14)
jaca视频教程 jaca游戏服务器端开发 Netty NIO AIO Mina视频教程 课程目录: 一、Netty快速入门教程 01、第一课NIO 02、第二课netty服务端 03、第三课netty客户端 04、第四课netty线程模型源码分析(一) 05、第五课netty线程模型源码分析(二) 06、第六课netty5案例学习 07、第七课netty学习之心跳 08、第八课protocol buff学习 09.第九课自定义序列化协议之自定义序列化协议 10、第十课自定义数据包协议 11.第十一课粘包分包分析,如何避免socket攻击 12.分析设计一个聊天室的小项目 二、java NIO,AIO编程视频教程 1、java NIO,AIO编程_01.flv 2、java NIO,AIO编程_02.flv 3、java NIO,AIO编程_03.flv 4、java NIO,AIO编程_04.flv 5、java NIO,AIO编程_05.flv 三、Java语言基础教程-Java NIO流篇 [第1节] Java NIO流-简介和概述.flv [第2节] Java NIO流-缓冲区.flv [第3节] Java NIO流-缓冲区操作.flv [第4节] JavaNIO流-通道1.flv [第5节] Java NIO流-通道2.flv [第6节] Java NIO流-socket通道操作.flv [第7节] Java NIO流-文件通道操作.flv [第8节] Java NIO流-选择器 .flv [第9节] Java NIO流-选择器操作.flv 四、Mina视频教程 00、Mina视频课程介绍.flv 01、Mina服务端helloWorld入门.flv 02、Mina客户端helloWorld入门.flv 03、Mina整体体系结构分析.flv 04、Mina学习之长短连接.flv 05、Mina学习之MinaIOService接口.flv 06、Mina学习之MinaIOFilter接口.flv 07、Mina学习之MinaIOSession接口.flv 08、Mina学习之MinaIOProcessor线程模型.flv 09、Mina学习之MinaIOBuffer接口.flv 10、Mina学习之自定义协议介绍.flv 11、Mina学习之自定义协议数据包分析.flv 12、Mina学习之自定义协议数据包实现.flv 13、Mina学习之自定义协议-编码器.flv 14、Mina学习之自定义协议-解码器.flv 15、Mina学习之自定义协议-服务端实例.flv 16、Mina学习之自定义协议-客户端实例.flv

110,533

社区成员

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

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

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