c#下多线程的tcp粘包问题,请高手指点思路。

wwwww112233 2014-06-23 04:56:22
服务器端需要不断的向客户发送不同类型的数据,比如学生信息,老师信息,成绩信息等等不同的数据。因为信息比较多比较大,客户端收到几种不同数据,很难拆包....
现在服务端使用 beginSend发送,但是这个异步发送,无法保证顺序;更关键的时候,当一个学生信息分为10个包发送的时候,这10个包还没有发送到,已经有另外5个学生的信息(假如是5个包)又在beginSend发送。

这样,客户端收到的信息产生如下问题:
收到的信息次序混乱,因为不知道收到的包属于哪个类型,这个包是什么顺序等等。
都是类型一样的包(学生),客户端如果保证能正常处理这些包。

我现在的处理方法:
服务端发送数据的时候,使用了一个byte位作为标记类型,使用了4个byte位标记数据长度,使用了8个byte标记数据的唯一id,再使用一个byte标记包的数量。
这样我收到包的时候,先判断他的类型,然后找到他的唯一id,把唯一id的作为索引,继续接收同一个id 同一个类型的包,知道接收所有的包,然后合并。

但是现在我担心的问题是:
数据那边我使用了14个byte位来做标记 ,也就是byte[14]。但是在发送数据过程中,万一这14个byte字节被切断了,比如某一次只收到5个字节,但是后面的包有黏在一起,那怎么办?
或者是否有更好的解决方案?

问题有点复杂,谢谢!!!!!
...全文
654 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
wwwww112233 2014-07-03
  • 打赏
  • 举报
回复
引用 25 楼 gomoku 的回复:
[quote=引用 23 楼 sp1234 的回复:] ...只要是 BeginSend 是一次性发送的整个消息,即使你的客户端并发10个线程去向对方同一个端口发送学生信息,对方也不会接收错误。
但是Socket.Send和Socket都不能保证一次性把整个消息发送出去。没有同步控制的多线程发送,有可能因为部分发送而出现数据错位,这种情况是客户端 不能纠正的(包括wwwww112233 说的‘添加发送标志’的方法也不能解决)。 [/quote][/quote] 我的发送端是多个BeginSend ,没有做同步控制发送(如用同步那我就不用BeginSend 了),所以问题复杂起来,但是性能却好起来。我添加的标志位,只是用于辨认数据类型,但无法保证数据是否乱,所以我为每次send的包又加了唯一标识,这样收到的时候无论顺序怎样我都可以还原。不过正如我问题所说,在某个情况下,确实会把我的标志位或唯一序号标识(4个字节)截断,这种情况,这个数据就会丢弃,无法还原。也不做重发请求。
gomoku 2014-07-01
  • 打赏
  • 举报
回复
引用 23 楼 sp1234 的回复:
...只要是 BeginSend 是一次性发送的整个消息,即使你的客户端并发10个线程去向对方同一个端口发送学生信息,对方也不会接收错误。
但是Socket.Send和Socket都不能保证一次性把整个消息发送出去。没有同步控制的多线程发送,有可能因为部分发送而出现数据错位,这种情况是客户端 不能纠正的(包括wwwww112233 说的‘添加发送标志’的方法也不能解决)。 MSDN关于Socket.Send:
引用http://msdn.microsoft.com/zh-cn/library/w93yy28a(v=vs.110).aspx
Send 将数据同步发送到 Connect 或 Accept 方法中指定的远程主机,并返回成功发送的字节数。...
MSDN关于Socket.BeginSend和EndSend:
引用http://msdn.microsoft.com/zh-cn/library/5f6fk8ze(v=vs.110).aspx
...如果 EndSend 的返回值指示未完全发送该缓冲区,请再次调用 BeginSend 方法,
sp1234_maJia 2014-06-30
  • 打赏
  • 举报
回复
这样即使客户端对某一个学生的信息使用多次Receive收到 --> 这样即使服务器端对某一个学生的信息使用多次Receive收到
  • 打赏
  • 举报
回复
假设你有一个客户端,它向100个服务器发送信息,假设要使用 BeginSend 连续发送1000个学生的信息,那么保证每一个学生的信息都使用一个BeginSend发送就行了。这样即使客户端对某一个学生的信息使用多次Receive收到,也可以保证是连续的,一定是收到一个学生的信息之后才收到下一个学生的信息。 假设你每一个学生的信息最后都用一个换行回车来结束,例如
var sb = new StringBuilder();
sb.Append(.....你的文本消息....);
sb.AppendLine();
var data = Encoding.UTF8.GetBytes(sb.ToString());
client.BeginSend(data,.....);
这里,假设消息内容以文本方式表示,并且组合在一起,最后一换行回车结束。 然后在服务器端,循环Receive数据时,一旦是换行回车最为buffer的最后两个字节,就可以对收到的消息立刻转为 MemoryStream,并且以循环 StreamReader.ReadLine() 方式读取出一行或者多行文本,每一个文本都是一条消息。 只要是 BeginSend 是一次性发送的整个消息,即使你的客户端并发10个线程去向对方同一个端口发送学生信息,对方也不会接收错误。
  • 打赏
  • 举报
回复
引用 3 楼 gomoku 的回复:
多线程往同一个Socket写数据会出问题的。 解决的方法就是,服务器端只允许一个发送者。 其他线程发数据到一个队列里,由发送者根据队列进行顺序发送。
晕倒。
  • 打赏
  • 举报
回复
引用 楼主 wwwww112233 的回复:
服务器端需要不断的向客户发送不同类型的数据,
看不懂你的这个逻辑。你认为什么是服务器?这个都错了,其它的东西肯定乱成一锅粥。
tcmakebest 2014-06-30
  • 打赏
  • 举报
回复
楼主的处理方式基本是正确的,我认为在TCP中处理多个数据包可以采用三种方式: 1 用特殊字符标识包头包尾,如果内部有这个字符,用转义,转义符本身也要转义 2 用包含长度的固定包头确定包的大小,然后收完一个包. 3 用XML形式,XML的根结点代表一个数据包,内部可以包含任意多的子结点,缺点是传输的是文本,数据密度不高.
wwwww112233 2014-06-30
  • 打赏
  • 举报
回复
引用 17 楼 jiaoshiyao 的回复:
异步也是可以有顺序的 异步1执行完成之后执行异步2 异步2执行完成之后执行异步3 不要 异步1 异步2 同时执行 我的后台客户端队列 就是这样的 处理完这个请求 再去处理下一个请求 虽然我不知道这样 到底是好还是不好
----你代码执行顺序确实是这样。但是事实上可能是异步2执行完后,先发送了异步2的数据,再发送了异步1的数据。
wwwww112233 2014-06-30
  • 打赏
  • 举报
回复
谢谢大家的讨论,因为发帖次日已经解决,所以一直没上来看。看了大家的帖子,学习到不少。 当然,如网上说的,连续send,定义好了协议,单线程发送自然不会粘包。问题是这样的代码拿去上课还可以,在项目中怎么敢用。 有朋友认为多线程用同一个socket发送,这个情况是会发生异常程序会断掉的,我问题中既然没有说异常,就说明我并没有这样使用。我是多线程调用多个独立的socket。 问题是n多客户端连接一个服务端,服务器高频实时的给n个客户端发送数据,不是一次性完事的问题。发送数据如果同步发送,性能影响很大。异步分包发送,无法保证顺序。很多朋友口口声声的保证顺序,那是同步模式下。简单的说,连续两个begainSend写着,连发送的顺序你也无法确定(千万不要说写在前面那句就一定会先发送),更不用说接受的顺序了。 我的解决办法和我之前问题帖子一样,办法一样,就是按那个办法解决了。其实就是用前面几位作为发送的一些标志。接受那边就可以根据这些信息重组包的顺序,保证类型和次序不乱。 我问题中问的是截断的问题,我不知道为什么有朋友认为不会截断。我debug下,缓冲区稍微设置小点,发送byte[100],都有可能第一次收到8个,第二次收到92个。 谢谢大家。
jiaoshiyao 2014-06-30
  • 打赏
  • 举报
回复
异步也是可以有顺序的 异步1执行完成之后执行异步2 异步2执行完成之后执行异步3 不要 异步1 异步2 同时执行 我的后台客户端队列 就是这样的 处理完这个请求 再去处理下一个请求 虽然我不知道这样 到底是好还是不好
wwwww112233 2014-06-30
  • 打赏
  • 举报
回复
引用 6 楼 jiaoshiyao 的回复:
粘包 情况 可以加校验 数据无序 。。。。Stream模式是有序的 是你后台代码无序 我的Socket 后台有一个队列 是有序的 哈哈 看样子你的都没有哈
校验加了,代码是有序的,但是多个异步发送确实无法保证发送顺序。这正是我的问题。
wwwww112233 2014-06-30
  • 打赏
  • 举报
回复
引用 4 楼 u010302605 的回复:
tcp有必要你这么搞么?你直接本地缓存下,然后永远是一个线程在发送接收数据,tcp本身就是滑窗可靠的通信,不会出现所谓的顺序混乱,粘包等问题。所以,我的楼上回答的是正解。楼主自己对tcp理解存在问题,还是在以串口的概念理解tcp。 如果你结贴散分,分给3楼吧,他的是正解。如果你对tcp还是不懂的话,可以直接私密我
你完全没看懂是所说的。tcp本身保证顺序,但是我问题说了,分包的发送是使用beginSend,这是异步发送多个包。
wwwww112233 2014-06-30
  • 打赏
  • 举报
回复
引用 3 楼 gomoku 的回复:
多线程往同一个Socket写数据会出问题的。 解决的方法就是,服务器端只允许一个发送者。 其他线程发数据到一个队列里,由发送者根据队列进行顺序发送。
可能是没把问题描述清楚,并不是多线程往同一个socket发送数据。 实际情况是,每个客户端都在连接server,server保存有每个客户端的soket。有消息通知的时候,server要给一批订阅消息的客户端发送消息。现在是因为消息更新是毫秒级别,而消息本身又分为多种类型,又不小。 想不到这个帖子引起大家讨论,其实发帖次日我自己已经解决。所以没有上来看
CGabriel 2014-06-24
  • 打赏
  • 举报
回复
包头加个长度就可以解决了 窗口滑动协议? 那个东西就是拿来加剧粘包的。 校验? TCP 是面向对连接的,可靠的协议,无需做多余的事情 无序?再重复一次,TCP 是面向对连接的,可靠的协议,所有数据包都是按序接受的。如果出现无序的情况,百分之一百是代码的 bug。
penguin_ku 2014-06-24
  • 打赏
  • 举报
回复
http://wenku.baidu.com/link?url=GMOnN8iUY8B-B-qZPygkW4wGMeebQOdDhk3g0KdzXo7dTLvS1NJHxohyaQFe3xqxMsuVQeqLci4BtzDfAHsf0H1HByxaxouZ9VEGmwKU1r7 都去看看,然后再回复这个帖子,不要小白的知识点还讨论得起劲
penguin_ku 2014-06-24
  • 打赏
  • 举报
回复
引用 8 楼 zdczdcc 的回复:
[quote=引用 4 楼 u010302605 的回复:] tcp有必要你这么搞么?你直接本地缓存下,然后永远是一个线程在发送接收数据,tcp本身就是滑窗可靠的通信,不会出现所谓的顺序混乱,粘包等问题。所以,我的楼上回答的是正解。楼主自己对tcp理解存在问题,还是在以串口的概念理解tcp。 如果你结贴散分,分给3楼吧,他的是正解。如果你对tcp还是不懂的话,可以直接私密我
tcp怎么理解呢[/quote] 去google下滑​动​窗​口​协​议,你会改变对于tcp的理解。tcp这么成熟的,都不知道封装到到完美的通信方式,竟然一堆人在讨论都被tcp已经搞定的问题。我真心无语。 另外,楼主的无序粘包纯粹自己不会用tcp,几个线程都并发得调用socket,天知道你下一个包发过来的是哪个线程的。所有要发送的内容压栈,然后一个发送线程,一个接收线程。这里不是C++的园地么,C++写通信,不是都是异步重叠或者完成端口类型的,里面对于socket的用法不是都很清楚的么
penguin_ku 2014-06-24
  • 打赏
  • 举报
回复
tcp竟然还有人在讨论粘包,更有人还在讨论截断。。。。。滑窗通信,tcp在发下一个包之前是询问对面是否有足够的缓冲区,如果没有,就不会发过去,怎么会截断,能不用串口的思维讨论tcp不???为何实时监控不用tcp,说tcp不实时,如果tcp都你们说的这样一大堆问题,还是可靠通信么? 能别自己没事整事坑自己么?
於黾 2014-06-24
  • 打赏
  • 举报
回复
引用 7 楼 jiaoshiyao 的回复:
[quote=引用 5 楼 Z65443344 的回复:] 万一被切断了?TCP不存在切断的问题 我测试的时候发送了100W字节的数组,一次性就能收到 问题就是你每次接收的字节数要足够,不要服务端向你发送了100字节,你每次只接收90字节,扔掉10字节还得自己拼接
服务器或者客户端的缓冲区 绝对不可能声明一个100W的字节数组 一次65535就足够了 假如有文件上传操作 大于65535 也是先接受到65535在接受一次 剩余的 如果缓冲区有90字节 而数据是100字节 那就是后边扔掉 自己拼[/quote] 所以说了,我那是做测试,就是想知道最多能发送多少数据不会被截断.测试到100W没问题,就不需要测试更大的了. 实际应用中仅仅是发送有限的数据,不发表结构和文件的话,通常1000就足够了. 所以只是提醒楼主,如果数据最大是100字节,你可以每次接收200,避免程序人为的将数据截断. 还有就是接收数据要及时.同步的采用while接收,异步的在收到数据的时候立即接收 而不是固定几秒去接收.那样不粘包才怪.
zdczdccccc 2014-06-24
  • 打赏
  • 举报
回复
引用 4 楼 u010302605 的回复:
tcp有必要你这么搞么?你直接本地缓存下,然后永远是一个线程在发送接收数据,tcp本身就是滑窗可靠的通信,不会出现所谓的顺序混乱,粘包等问题。所以,我的楼上回答的是正解。楼主自己对tcp理解存在问题,还是在以串口的概念理解tcp。 如果你结贴散分,分给3楼吧,他的是正解。如果你对tcp还是不懂的话,可以直接私密我
tcp怎么理解呢
jiaoshiyao 2014-06-24
  • 打赏
  • 举报
回复
引用 5 楼 Z65443344 的回复:
万一被切断了?TCP不存在切断的问题 我测试的时候发送了100W字节的数组,一次性就能收到 问题就是你每次接收的字节数要足够,不要服务端向你发送了100字节,你每次只接收90字节,扔掉10字节还得自己拼接
服务器或者客户端的缓冲区 绝对不可能声明一个100W的字节数组 一次65535就足够了 假如有文件上传操作 大于65535 也是先接受到65535在接受一次 剩余的 如果缓冲区有90字节 而数据是100字节 那就是后边扔掉 自己拼
加载更多回复(6)

110,539

社区成员

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

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

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