作者:Kevin Lynx 来源:C++博客   酷勤网收集 2008-05-22

摘要
  Etwork是一个很小巧的网络库。Etwork基于select模型,采用我之前说的技巧,理论上可以处理很多连接(先不说效率)。如同很多网络库一样,总会有一个类似于ISocketManager的类,用于管理所有网络连接(当用户服务器时)。而ISocket则用于代表一个网络连接。

从开始接触网络编程这个东西开始,我就不间断地阅读一些网络库(模块)的源代码,主要目的是为了获取别人在这方面的经验,编程这东西,还是要多实践啊。

基本上,Etwork是一个很小巧的网络库。Etwork基于select模型,采用我之前说的技巧,理论上可以处理很多连接(先不说效率)。

先看看下这个库的结构:

classgraph

如同很多网络库一样,总会有一个类似于ISocketManager的类,用于管理所有网络连接(当用户服务器时)。而ISocket则用于代表一个网络连接。在其他库中,ISocketManager对应的可能就是Server,而ISocket对应的则是Session。

在接口设计上,尽管Etwork写了很多接口类(看看那些IClass),但是事实上它抽象得并不彻底。只是暴露给客户端的代码很简洁,而库本身依然臃肿。不知道为什么,现在我比较喜欢纯C这种简洁的东西,对于OO以及template,渐渐地有点心累。

在功能实现上,我以TCP服务器为例,CreateEtwork根据传来的参数建立服务器,在SocketManager::open中是很常规的socket, bind, listen。当建立了服务器之后,需要在程序主循环里不断地轮询状态,这里主要调用poll函数完成。

poll函数主体就是调用select。当select成功返回活动的套接字数量后,Etwork依次轮询读、写、错误fdset,将保存的所有网络连接(就是那些ISocket对象)对应的套接字与fdset中当前的套接字做比较。大致逻辑为:

 

fd_count = select( 0, readset, writeset, exceptset, &timeout ); 

for( each fd in readset )
    
if( fd is listening fd ) 
        accept 
new connection
    
else
        
for( each socket in all connections )
            
if( fd == socket )
                can read data on 
this socket 

for( each fd in writeset )
 

for( each fd in exceptset )
 
没什么特别让人注意的地方(别觉得别人垃圾,耐心读别人的代码不是什么坏事)。每一次,当Etwork检测到新的连接时,会创建新的ISocket对象,并关联对应的套接字,然后保存此对象到一个列表中。当poll结束后,客户端程序通常会调用accept函数(Etwork中提供的接口),该函数主要是将poll中保存的新的ISocket对象全部拷贝出去。

在接收、发送网络数据上,Etwork如同几乎所有的网络库(模块)一样,采用了缓冲机制。这里所说的缓冲机制是,网络模块接收到网络数据时,将数据保存起来,客户端程序想获取数据时,实际上就是从这个缓冲中直接取,而不是从网络上获取;同理,发送数据时,客户端程序将数据提供给网络模块,网络模块将数据保存起来,网络模块会在另一个时候发送这个缓冲中的数据(对于异步IO的处理毕竟不一样)。

Etwork关于这个缓冲机制的相关代码,主要集中在Buffer这个类。与Buffer相关的是一个Message机制。Buffer维护了一个Message的队列(deque)。一个Message实际上是一个非常简单的结构体:

struct Message 
{
    unsigned 
short offset_;
    unsigned 
short size_;
}

 

Message * m = (Message *)::operator new( size + sizeof( Message ) ); 

 这其实是一个很危险的做法,但是从Etwokr的源码可以看出来,作者很喜欢玩弄这个技巧。与Buffer具体相关的接口包括:get_data, put_data, get_message, put_message。Buffer内部维护的数据都是以Message的形式组织。但是,对于外部而言,却依然是raw data,也就是诸如char*之类的数据。几个相关函数大致上的操作为:获取指定尺寸的消息(可能包含多个消息),将一段数据加入Buffer并以消息的形式组织(可能会创建多个消息),将一个消息以raw data的形式输出,将raw data以一个消息的形式加入到Buffer。

一般情况下,Etwork的poll操作,会将套接字上的数据接收并put_data到缓冲中;发送数据时则get_data。客户端要从缓冲中获取数据时,就调用get_message;发送数据时就put_message。

Etwork中还有一个比较有趣的东西:marshaller。这个东西主要就是提供将C++中各种数据类型的变量进行字节编码,也就是将int long struct之类的东西转换为unsigned char,从而方便直接往网络上发送。

基本上,Buffer和marshaller可以说是一个网络库(模块)的必要部件,你可以在不同的网络库中看到类似的东西。

Etwork在网络事件的处理上,除了上面的轮询外,还支持回调机制。这主要是通过INotify,以及给各个ISocket注册Notify对象实现。没什么难度,基本上就是observer模式的简单实现。

其他东西就没什么好说的了,纵观一下,Etwork实现得还是比较典型的,可以作为开发网络库的一个简单例子。

来自:剖析Etwork网络库

 这其实是消息头,在消息头后全部是数据。在创建消息时(new_message),Etwork根据客户端提供的数据创建足够大的缓存保存:

分类: 网络编程 3G技术 IPv6技术 网络技术 网络管理员 黑客技术



关于酷勤 | 联系方式 | 免责声明 | 友情链接