loop_in_codes

低调做技术__欢迎移步我的独立博客 codemaro.com 微博 kevinlynx

剖析Etwork网络库

Author : Kevin Lynx

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

基本上,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_;
}

 

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

 

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实现得还是比较典型的,可以作为开发网络库的一个简单例子。

posted on 2008-05-21 21:06 Kevin Lynx 阅读(6963) 评论(5)  编辑 收藏 引用 所属分类: network

评论

# re: 剖析Etwork网络库 2008-05-21 21:52 空明流转

做面向对象大抵就是两个极端,一个是回归过程化的抽象,一个就是纯面向对象。

不过感觉这个倾向是跟你长期从事的领域有关。  回复  更多评论   

# re: 剖析Etwork网络库 2008-05-21 23:04 eXile

我也是越来越不喜欢C++了,目前比较倾向于纯C或Python。
对于纯C的网络库,可以试试 libevent 或 libev, 应该算是比较好的库了。  回复  更多评论   

# re: 剖析Etwork网络库 2008-05-22 10:42 关中刀客

很多时候底层还是自己写比较的好~~~  回复  更多评论   

# re: 剖析Etwork网络库 2008-05-22 17:56 bugs_killer

ACE设计得就很好啊..我基于ACE裁剪过一个轻型的通信库.
http://code.google.com/p/netdkit/

自我感觉很好啊.  回复  更多评论   

# re: 剖析Etwork网络库 2009-09-29 10:47 libev

libev是网络库?@eXile
  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理