Friday, September 26, 2008

Diff UNIX Windows, Part 1

前几天尝试着移植CTorrent-dnh到Windows上,主要是想了解一下平台的差异性以及BT的基本原理。后来发现改动量比预计的大,就放弃了。其中还是有不少收获,以后有机会一定要多读代码。

CTorrent
是C++实现的bt客户端,可以在各种UNIX系统上跑。2004年就停止更新了。而CTorrent-dnh作为加强版,修正了一些BUG并且增加了功能。原版结构比较简单,比较适合结合BT协议来阅读。

Windows 的API经常换,光是网络这块就已经有好几套了。而UNIX下最早的select/poll模式却一直用到了现在。虽然epoll,kqueue这些有更 好的性能,但是对于简单的BT客户端,select就够用了,至少CTorrent是这样的。Windows下也支持select模式,但是不遵守规则似 乎也是MS的惯例。下面收集了一些例证:

1)UNIX下SOCKET类型定义为int,而Windows下则是UINT_PTR。这样下面这段UNIX上正常的代码就不起作用了:

SOCKET maxfd = -1;
for (i=0; i maxfd) maxfd = sk[i];

因为UINT_PTR是无符号的,因此-1实际上是最大值。

2)其实不用为上面的情况担心,因为Windows直接忽视select函数的第一个参数:)

3)跨平台移植代码最怕的不是缺了什么函数,而是函数相同却功能却不同。以select函数为例。

Berkeley-derived implementations (and POSIX) have the following two rules regarding select and nonblocking connects:

  1. When the connection completes successfully, the descriptor becomes writable (p. 531 of TCPv2).

  2. When the connection establishment encounters an error, the descriptor becomes both readable and writable (p. 530 of TCPv2).

------ UNIX Networking Programming Volume 1

If a socket is processing a connect call (nonblocking), failure of the connect attempt is indicated in exceptfds (application must then call getsockopt SO_ERROR to determine the error value to describe why the failure occurred).
------ MSDN

应该说Windows的这种改变并非不合理,因为在UNIX下exception fdset名不符实,用处太少,以至于经常被设为NULL而忽略。但这种既不是修BUG又不提高性能的改变却是以牺牲兼容性为代价的。

4)UNIX 下descriptor是通用概念,不仅仅是socket,因此select可以同时管理不同类型的descriptors。而Windows的 select只能接受SOCKET类型。这也是CTorrent-dnh难以移植的原因之一,它的select还管理标准输入输出。

5)UNIX的errno不是线程安全的,因此Windows使用WSAGetLastError函数代替。注意errno仍然存在,但是意义已不同了。

6)getsockopt函数的optval参数在UNIX下是int*类型,WINDOW下则是char*类型。这个比较想不通。

7)Windows下在使用网络API前后必须分别调用WSAStartup和WSACleanup函数。

No comments: