一个简单的完成端口网络库设计小结

这几天想把公司平台网络层换成完成端口的,就先写了一个简单的完成端口网络库,目前只实现了TCP的部分,UDP的部分比TCP略简单。

网路库分两个部分,util和data,也就是工具类和数据类。工具类负责socket的创建、监听、数据收发、资源释放等,数据类主要负责数据的接收转储。

工具类的主要内容在上一篇IOCP小结中写过了,数据类只是封装了两个缓冲,所以这里只小结一下开发中遇到的问题:

1、开始设计的时候,在数据类里面有两个list<WSABuf>队列,一个recv一个send,len接收时为4096,发送时为0。如果初始化WSABuf时不小心把len初始化为0 ,则不论buf多大,都不会接收数据。发送时调用一次接口,写入一包数据,如果是协议数据,则直接发送,如果是音视频数据,则缓冲5包后发送,以减少WSASend的调用。后来测试发现不用做缓冲,可以直接WSASend,因为TCP底层有缓冲,可以控制发送速度。所以取消了list的限制,直接使用两个WSABuf。经过进一步测试发现,在send或recv时传入的WSABuf可以是局部变量,只要它所指向的内存空间在网络通信结束前(GetQueuedCompletionStatus返回前)一直存在即可。所以把类成员从WSABuf改为vector。修改后的data类中,有两个缓冲,vector<char> sendbuf,vector<char> recvbuf,初始化时需要给recvbuf分配空间(经验值是4096,对TCP和UDP都适用),否则recv会收不到数据。

2、数据接收和发送的时机:IOCP是异步的,所以需要通知。工具类提供对外接口用于绑定接收回调,上层应用传入回调函数后,当接收到数据时调用回调函数(function对象)。这样上层应用不用关心数据什么时候到达,只需要在回调中对到达的数据进行处理。发送的时候,先调用data类的addData接口,向发送缓冲写入数据,同时标明数据类型(UDP_DATA,TCP_DATA),然后调用util类sendToRemote接口,根据数据类型调用WSASend或者WSASendTo 发送数据。

3、util类被设计为data的友元类,这样在util类中可以直接访问data类的私有成员和函数。开始设计的时候,在util也加入data友元类,在data的addData中发送数据,结果发现类互相引用的问题。然后修改为通知方式,util把发送函数注册到data类中,在data的addData中调用注册的发送函数来发送数据。但是这时发现,这样其实违背了功能单一性原则,所以最后采用准备数据和发送完全分离的方式。

4、完成端口WSARecv和WSASend的投递,单个socket尽量一次投递一个。如果同时投递,不能保证回调返回的顺序,可能造成处理的混乱。也就是在上层架构设计的时候,协议的交互最好是有序的。我们的上层应用,协议是同步的,一收一发,数据是只发或只收,所以网络层实现更简单。越是面向应用开发底层库,性能越高。

5、最后完成的data类中,只有一个socket,两个缓冲。

6、util类负责TCP、UDP、组播socket的创建,Accept Socket的创建,最后socket都被保存在data中。释放socket时,data把socket传入util的释放函数进行释放。

以上是一个简单的完成端口网络库,util全局可以只有一个(因为完成端口可以全局只用一个实例),每个数据类保有数据和socket,整个结构竟然有点儿像boost.asio的简化版。


猜你喜欢

转载自blog.csdn.net/blwinner/article/details/53018675