Solaris 平台上開發網路程式的一些心得

Last Update: 12/08/2003

UNIX 網路程式設計的聖經 Steven 第二版就是以 Solaris 為它的平台,所以基本上在 Solaris 上寫程式只需要看這本聖經就足足有餘,其他的書可以供起來。唯二必須注意的地方:

1. setsockopt() 裡沒有 SO_RCVTTIMEO 和 SO_SNDTIMEO 可以用,因此,若想要替 socket 設 timeout 的話,只剩下兩種方法。一種是利用 per-thread alarm(),再不然就是利用 select()。我個人認為 select() 是比較正規的做法。

2. 有些域名解析 (resolving) 的函數在新版 Solaris 中並未提供,例如 inet_aton() 函數。我們可以用更廣用的 inet_pton() 來代替 inet_aton(),其他未提供的函數也有相似的對應。

一些觀念的澄清

若將 socket 設為 blocking,自然無法設定 timeout (TCP protocol 的限制,請參考 Comer 教科書)。所以最基本也最容易的做法便是使用 non-blocking socket + select(),可以很容易地在 UNIX Socket Programming FAQ 中找到範例,或是拜請咕狗大神也可以。

但若在 server 端使用了 select() 控制 timeout 的話,client 端也必須配合使用 SO_LINGER,不然 TCP protocol handshaking 會斷掉,也就無法收到完整的訊息了。server 端若是用 non-blocking socket 來 accept 的話,收進來的 socket 也是 non-blocking,因此 client 端也必須使用 SO_LINGER 來配合 non-blocking accept.

討厭的 Race Condition

在 Solaris 系統中,kernel 會盡快盡可能地回收用過的 socket file descriptor (socket fd),這種機制可能會造成下面的 race condition:

1. Server 端程式 spawn 一個新的 thread 去 accept incoming connection,可是 Client 端並未設定 SO_LINGER,所以根據 TCP protocol 的規定,Client 端在做完 send() 之後,立刻送出 RST 信號到 Server 端的 accepted socket。

2. Server 端 Solaris kernel 收到 RST 信號,立即將對應的 fd 標示為可以回收再利用。

3. Server 端又收到新的 connect request,於是將這個回收的 fd 指定給這個新的 connection,準備 spawn 一個新的 thread。

4. 在 1 中的 thread 要結束,C++ destructor 啟動執行 fclose(fd)。於是新的 connection 在被 accept 到一半的同時就很快樂的收工了。

這個 Race Condition 解決的方法很多,我們可以做 connection-pooling 或是利用其他的方法來解決,視當時的狀況而定,並無特定的公式解法。


有心得要討論嗎?歡迎 mail 給我。