Creative Commons License
本Blog采用 知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议 进行许可。 —— Fox <游戏人生>

游戏人生

游戏人生 != ( 人生 == 游戏 )
站点迁移至:http://www.yulefox.com。请订阅本博的朋友将RSS修改为http://feeds.feedburner.com/yulefox
posts - 62, comments - 508, trackbacks - 0, articles - 7

ACE: Socket封装(01)

Posted on 2009-09-01 14:22 Fox 阅读(3864) 评论(3)  编辑 收藏 引用 所属分类: T技术碎语

本文同步自游戏人生

o *__ 序 __* o

在阅读ACE代码和C++NPv1, v2, APG的时候,我意识到一个问题:虽然稍有C++和网络基础的同学都可以读懂ACE,但如果你对OS(五大管理模块都包含在内)、TCP/IP、C++、Design Patterns了解越多,你就越能体会ACE为什么需要这么庞杂,虽然它不够完美(但至少我还没有资格来批评这一点,我现在最常想做的一个动作就是五体投地)。

而且我隐约感觉到,我现在所写的很多东西在以后(对于有些人或许就是现在)看来会相当不深刻、相当不严谨,但对于一段学习历程,这个过程是必然的、必需的。

在C++NPv1中,Douglas C. Schmidt把原始socket及其API的缺陷有些妖魔化了,比如一段加上注释、空行在内的35行的代码,被指出有10处错误之多。这就像很多其他语言的倡导者或反传统C/C++指针者在批评指针时的说法一样。长期使用原始socket和指针的同学对此感觉很不舒服,何况socket API提供了大量错误检测的接口,至多是不够友好罢了。你好就好了,没必要抓住别人一顿痛批吧,『本是同根生,相煎何太急』。

虽然Solaris、Linux的很多版本及Windows对起源于Berkeley的socket API进行了重写,但不可否认,由于历史原因和POSIX标准的存在,对于使用者而言,我们可以无视这些API的实现差异。只是一旦我们从socket通信扩展到其他IPC通信的话,就需要正视各种I/O细节的差异了。

由于UNIX中,对于socket, file, pipe, device的大多数操作,描述符都是通用的(这一点,OS上面讲的更清楚些)。而Windows中,句柄大多不能互换(socket对于MS来说是舶来品)。系统和标准的不一致导致地址、协议和API的混杂甚至混乱。

UNIX下的描述符和Windows的句柄可以看作是同一个概念,只是应用环境不一样,所描述的内容也时常不一样,再简单了说,它们都是一个整型的ID。

ACE的源码中使用了大量预处理指令,尤其在跨平台/编译环境的部分更加明显。鉴于C/C++标准的博大胸怀,有些指令需要阅读相关编译器提供的帮助文档:

o #pragma: GCC, MSVC

o #define (#, #@, ##) : GCC, MSVC

其中有若干代码文件以.inl为后缀,里面是对部分函数的内联实现,以使代码结构看上去更加简洁。如果确定使用内联函数的话,*.inl将被包含于*.h的最后,如果不使用,则像*.h一样,包含于*.cpp的头部。

ACE采用doxygen输出文档,在阅读代码注释时能够感受到差异,但基本不会影响阅读。

o * __ 关于第3章(C++NPv1)__ * o

ACE抽象的地址类ACE_Addr拥有ACE_DEV_Addr, ACE_FILE_Addr, ACE_INET_Addr, ACE_SPIPE_Addr, ACE_UNIX_Addr五个子类。对于狭义上的网络通信(TCP/IP)而言,ACE_INET_Addr对应于我们熟悉的sockaddr_in。

ACE_IPC_SAP是IPC(interprocess communication)I/O操作类的root类。

从编码的角度看,这个类漂亮的地方在于示例了抽象类的另一种实现方式。

一提到抽象类,大多数人的第一反应是pure virtual function。当一个基类确定需要使用virtual function时,这是一个不错的选择。但我们都知道虚拟函数有开销。而且对于一个结构简单的抽象基类和其继承子类(尤其是大量使用时),一个虚函数表带来的开销会让整个设计显得十分蹩脚。

我们都知道如何强制让一个类无法使用default constructor(protected)。如果对基类使用该方法,仅使子类具有public的default constructor,这就达到了定义抽象基类的效果。

virtual destructor的意义在于防止delete父类指针(指向子类对象)时未调用子类destructor。在此例中,为避免这种情况,同样将destructor声明为protected即可。

从设计实现的角度看,相较于socket API,ACE_IPC_SAP的子类ACE_SOCK提供了编译时对句柄合法性的检测。

从逻辑功能层面划分,socket有三种角色:

o active connection role (connector):主动连接

o passive connection role (acceptor):被动连接

o communication role (stream):数据通信

但socket API毕竟不是OOD出来的,对于一个socket描述符,也完全没有必要去限制其担负的功能,更不可能搞成三种不同的socket。而OOD的ACE则可以轻易实现对socket对象及其操作的封装。

工厂类ACE_SOCK_Connector是一个主动创建通信端的工厂类。socket API中的connect接口只是为一个socket建立与其它peer的网络连接,而不产生新的socket实例,也不依赖于任何其它socket。同样,ACE_SOCK_Connector只是为一个ACE_SOCK_Stream对象(对用于数据通信的socket的封装)连接到ACE_Addr(对struct sockaddr的封装)提供接口,也不含对ACE_SOCK_Stream对象的其它操作。

工厂类ACE_SOCK_Acceptor是一个被动创建通信端的工厂类。当监听到新的网络连接后,为该连接初始化一个ACE_SOCK_Stream对象。和connector不同的是,acceptor依赖于一个已经存在的充当监听功能的socket句柄(ACE_SOCK),因此,ACE_SOCK_Acceptor是ACE_SOCK的一个子类。

ACE_SOCK_Stream是只负有通信传输功能的socket,对应connection-oriented的TCP通信格式stream,和UDP的CE_SOCK_CODgram相呼应。ACE_SOCK_Stream只是socket的通信载体,在两个工厂ACE_SOCK_Connector和ACE_SOCK_Acceptor中初始化。这样一个类除支持最基本的数据发送(send)和接收(recv)和阻塞(blocking)、非阻塞(nonblocking)及定时(timed)的I/O模式外,还支持分散读取(scatter-read)和集中写入(gather-write)。

对于一个简单的『网络课程作业:写一个有连接的IM小程序』,上面这些内容已经足够了。当然即使使用对应的几个socket API也已经足够了。但我们显然更加关心如此庞大的一个库,是如何解决复杂的网络应用的,我尤其关心的是多线程并发如何更好的处理。

所以,我准备跑到第8、9章了。

Feedback

# re: ACE: Socket封装(01)  回复  更多评论   

2009-09-01 16:10 by 99书城
很好啊~~

# re: ACE: Socket封装(01)  回复  更多评论   

2009-09-01 17:15 by yleesun
不错,很真实

# re: ACE: Socket封装(01)  回复  更多评论   

2012-08-17 22:14 by swiss replica watches
清爽的很

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