0 . 开始
开发基于TCP的网络应用时,从字节流中拆解出一个个的数据包可以说是非常核心的任务,这一步做不好其他的无从谈起。本文从实战的角度来讲解C# .NET框架下的拆包逻辑应该如何写。
1. 同步 OR 异步
.NET框架中Socket操作函数有很多,同步和异步操作框架均有提供。笔者对c#的异步接口不甚了解,所以只用同步阻塞的方式来实现这个小程序。
2. 线程?
Socket操作函数既然是同步阻塞的,那么为了防止阻塞业务线程(一般说来就是进程的主线程啦),将Socket的同步阻塞式操作函数放在一个单独的线程中执行就是必然的了。
3. IOLoop 模型
基于1和2的分析,我们的io loop模型就是 线程 + 阻塞调用。假如刚刚接触了一门新编程语言,而这种语言的网络API又很丰富,在搞不清楚细节的情况下,线程+阻塞式API往往是正确的组合(IO复用函数本身也是阻塞式)。例如csharp的 BeginRecive方法,它是怎样实现异步的?,它会开启一个线程呢?还是开启一个线程池呢?,如果是一个线程池用在客户端就是浪费了!所以笔者选择了一个自认为老套但可靠的方式,好处是线程这类宝贵资源可以自己掌握,没有不确定性。
4. 网络协议
从连续不断的字节流中拆解出一个个的消息包,网络协议是必不可少的。网络协议定义了消息包的边界。笔者的协议为 : 长度[4字节] + 消息内容, 长度的值不包含长度本身占用的字节数, 长度的值就是消息内容的长度。
4. 消息缓冲区
缓冲区这个组件几乎是所有线上网络应用必备的,它一般用一个字节数组实现,它的作用就是保存从网络中读到的或是要发送到网络中的数据。因为每次read(write)时读到的数据不一定是一个完整的消息包,有可能本次读到的数据属于N个消息包,所以我们需要将读到的数据按照先后顺序放在一个缓冲区中,然后按照网络协议从这个缓冲区中切出一个个的网络包。笔者参考陈硕的muduo库实现了这个消息缓冲区。muduo
5. 测试
拆包写好后,远端按照网络协议组织好数据开始发送,网络环境是不受控的,对端发一次数据,我们可能要N次读才能读完,基于这样的事实来测试拆包逻辑。
我们在服务器端组织测试数据,在客户端测试拆包逻辑。服务端启动后生产N个数据包,每个数据包的长度是随机的。然后将这N个数据包按顺序存储到一个缓冲区中,待客户端连接后,从这个缓冲区中取出随机长度的字节流发给客户端,客户端从字节流中拆解出这N个包。由于服务端每次发送的长度是随机的,所以每次发送的数据可能属于一个数据包也可能属于M个数据包,就是说数据包的数据是混在一起的,客户端能够从这些混乱的字节流中正确的拆解出一个个的数据包,就说明我们的拆包逻辑是OK的。
6. 代码
https://github.com/code1w/tomnet-tcp-test.git
网友评论