NewLife.Net——管道处理器解决粘包
Tcp网络编程,必须要解决的一个问题就是粘包,尽管解决办法有很多,这里讲一个比较简单的方法。
老规矩,先上代码:https://github.com/nnhy/NewLife.Net.Tests
一、管道处理器
新建管道处理器项目HandlerTest,源码复制自第一节课的EchoTest项目,增加一个管道处理器类
class EchoHandler : Handler {public override Object Read(IHandlerContext context, Object message){var session = context.Session;var pk = message as Packet;session.WriteLog("收到:{0}", pk.ToStr());// 把收到的数据发回去 session.Send(pk);return null;} }
EchoHandler继承自处理器基类Handler,重载Read方法,当网络层收到数据包时,会调用该方法。
这里我们实现了Echo功能,并打印日志。返回null告知不再执行管道上的后续处理器。
既然有了处理器,第一节课中的MyNetServer就用不上啦,在TestServer中改回来标准的NetServer
// 实例化服务端,指定端口,同时在Tcp/Udp/IPv4/IPv6上监听 var svr = new NetServer {Port = 1234,Log = XTrace.Log }; svr.Add<EchoHandler>(); svr.Start();
这里的svr.Add<EchoHandler>()把上面的处理器给注册进去,大意就是由这个处理器来负责处理收到的网络数据包。
跑起来服务端和客户端看看效果:
可以看到,收发正常!
二、粘包的产生
真实应用场景中,不可能允许我们间隔1秒才发出一个网络包,直接就不该有等待。连续发送多个数据包,就很容易产生粘包。
static void TestClient() {var uri = new NetUri("tcp://127.0.0.1:1234");//var uri = new NetUri("tcp://net.newlifex.com:1234");var client = uri.CreateRemote();client.Log = XTrace.Log;client.Received += (s, e) =>{XTrace.WriteLine("收到:{0}", e.Packet.ToStr());};client.Open();// 定时显示性能数据_timer = new TimerX(ShowStat, client, 100, 1000);// 循环发送数据for (var i = 0; i < 5; i++){//Thread.Sleep(1000);var str = "你好" + (i + 1);client.Send(str);}//client.Dispose(); }
这里注释了睡眠语句,让它紧密发出5个数据包。注释后面的Dispose,让其有机会收到响应包。
跑起来看到,粘包了!!!
客户端发送5次,服务端作为一个包给接收了,整体处理,然后返回给客户端。
粘包的解决办法很多,一般是加头部长度或者分隔符,也有取巧的办法直接设置NoDelay。
从使用上来讲,相对可靠的做法是加头部长度。因为除了多个包粘在一起,还可能出现一个包被拆成两半,分别在前后两个包里面。
三、普通粘包解法
我们加上头部长度来解决解包问题。
修改一下服务端,增加一个处理器
static void TestServer() {// 实例化服务端,指定端口,同时在Tcp/Udp/IPv4/IPv6上监听var svr = new NetServer{Port = 1234,Log = XTrace.Log};//svr.Add(new LengthFieldCodec { Size = 4 });svr.Add<StandardCodec>();svr.Add<EchoHandler>();// 打开原始数据日志var ns = svr.Server;ns.LogSend = true;ns.LogReceive = true;svr.Start();_server = svr;// 定时显示性能数据_timer = new TimerX(ShowStat, svr, 100, 1000); }
StandardCodec处理器是新生命团队标准封包。https://github.com/NewLifeX/X/tree/master/NewLife.Core/Net
其固定4字节作为头部,其中后面两个字节标识负载长度。
也可以使用LengthFieldCodec编码器(如上注释部分),并制定头部加4字节作为长度。
编码器顺序非常重要,网络层收到数据包以后,会从前向后走过每一个处理器;SendAsync/SendMessage发送消息时,会从后向前走过每一个过滤器,逆序。
客户端也要增加相应过滤器
static void TestClient() {var uri = new NetUri("tcp://127.0.0.1:1234");//var uri = new NetUri("tcp://net.newlifex.com:1234");var client = uri.CreateRemote();client.Log = XTrace.Log;client.Received += (s, e) =>{var pk = e.Message as Packet;XTrace.WriteLine("收到:{0}", pk.ToStr());};//client.Add(new LengthFieldCodec { Size = 4 });client.Add<StandardCodec>();// 打开原始数据日志var ns = client;ns.LogSend = true;ns.LogReceive = true;client.Open();// 定时显示性能数据_timer = new TimerX(ShowStat, client, 100, 1000);// 循环发送数据for (var i = 0; i < 5; i++){var str = "你好" + (i + 1);var pk = new Packet(str.GetBytes());client.SendAsync(pk);} }
发送函数改为SendAsync,原来的Send(Packet pk)会绕过管道处理器。
客户端接收时,e.Message表示经过处理器处理得到的消息,e.Packet表示原始数据包。
同时,通过LogSend/LogReceive打开收发数据日志。
上图效果,客户端发出第5个包,头部多了4个字节,其中07-00表示后续负载数据长度为7字节(NewLife)。
服务端先收到第一个包11字节,然后收到44字节,这是4个包粘在一起。
然后StandardCodec编码器成功将其拆分成为4个,并依次通过EchoHandler。
到了客户端这边,也是后面4个粘在一起,并且也得到了正确拆分。
如果一个大包被拆分为几个,StandardCodec也能缓冲合并,半包超过500~5000ms仍未能组合完整时将抛弃。
四、总结
借助管道处理器架构,我们轻易解决了粘包问题!
显然,管道架构并非单纯为了粘包问题而设计,它有着非常重要的意义,加解密、压缩、各种协议处理,等等。
管道架构的设计,参考了Netty,因此大部分Netty的编解码器都可以在此使用。
NewLife.Net——管道处理器解决粘包相关推荐
- 【Netty】Netty解决粘包和拆包问题的四种方案
在RPC框架中,粘包和拆包问题是必须解决一个问题,因为RPC框架中,各个微服务相互之间都是维系了一个TCP长连接,比如dubbo就是一个全双工的长连接.由于微服务往对方发送信息的时候,所有的请求都是使 ...
- Netty 解决粘包和拆包问题的四种方案
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://my.oschina.net/ ...
- Netty解决粘包和拆包问题的四种方案
在RPC框架中,粘包和拆包问题是必须解决一个问题,因为RPC框架中,各个微服务相互之间都是维系了一个TCP长连接,比如dubbo就是一个全双工的长连接.由于微服务往对方发送信息的时候,所有的请求都是使 ...
- netty 高低位转码_Netty解决粘包和拆包问题的四种方案
在RPC框架中,粘包和拆包问题是必须解决一个问题,因为RPC框架中,各个微服务相互之间都是维系了一个TCP长连接,比如dubbo就是一个全双工的长连接.由于微服务往对方发送信息的时候,所有的请求都是使 ...
- Netty是如何解决粘包和拆包问题的
本文来说下Netty是如何解决粘包和拆包问题的 文章目录 概述 粘包和拆包 常见解决方案 Netty提供的粘包拆包解决方案 FixedLengthFrameDecoder LineBasedFrame ...
- 8-14-粘包问题、(通过简单报头)解决粘包问题、定制复杂报头
今日内容:1.流式协议=>粘包问题(***)解决方案:自定义应用层协议注意:head+datahead的长度必须固定2.基于udp协议的套接字通信先启动客户端是否可以???服务端是否需要list ...
- 什么是粘包和拆包,Netty如何解决粘包拆包?
Netty粘包拆包 TCP 粘包拆包是指发送方发送的若干包数据到接收方接收时粘成一包或某个数据包被拆开接收. 如下图所示,client 发送了两个数据包 D1 和 D2,但是 server 端可能会收 ...
- 10.python网络编程(解决粘包问题 part 2)
一.什么时候会产生粘包现象. 只有在使用tcp协议的情况下才会产生粘包现象!udp协议永远不会! 发送端可以1k1k的把数据发送出去,接收端,可以2k2k的的去接收数据,一次可能会接收3k,也有可能1 ...
- c# socket 解决粘包,半包
处理原理: 半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度时,将消息临时缓存起来,直到满足长度再解码 粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然后再判断 ...
最新文章
- Python技巧之函数拆包裹
- 神经网络最优化方法比较(代码理解)
- 在线rss阅读聚合器lilina-0.7安装笔记
- SAP Spartacus里,点击checkbox右边的span文本,不会触发checkbox勾选的原因
- Hibernate问题浅析
- Bootstrap3 弹出提示插件的方法
- 蠕虫病毒往往是通过进入计算机系统,2011年上海市高校计算机等级考试1级模拟卷题目...
- win11小键盘怎么设置 Windows11小键盘的设置方法
- python 遍历文件夹下文件修改并保存_利用python完成自动化的任务之遍历文件夹修改文件之后并保存备份...
- SI 9000 及阻抗匹配学习笔记(三)
- Java字符串排序(根据字节及字符长度进行排序)
- 计算软件介绍siesta、vasp、wien2k、PWSCF、Materials Studio
- eclipse中创建一个server使用tomcat服务
- AI时代,陪孩子玩什么游戏?| 前Google资深工程师实战心法
- 怎样看开源代码版权_版权声明在开源代码中泛滥成灾
- 基于MATLAB的分子相互作用的表征模型
- IAR6.3创建MSP430工程
- 07-Web storage
- 人机大战(类和对象)
- contiki学习笔记 etimer部分