相信鄉民們一定有那種和別人約好要見面,可是卻苦等不到對方的經驗。如果是男女朋友約會,通常是男方比較早到,女方遲到個 30 分鐘算是正常,遲到 1 - 2 個小時也不算太超過。在熱戀中的男方,心目中的 timeout 可能是 > N 小時,但是一旦
不管如何,具有 timeout 的觀念是很重要的,真實世界沒有什麼『不見不散』,『等你一生一世』的那種事,像『王寶釧』這種把 timeout 設成『18 年』以上的人,已經算是瀕臨絕種的稀有寶物了。
***
Timeout 對於 programmers 而言也是很重要的觀念,之前 Teddy 在『還少一本書:Release It! Design and Deploy Production-Ready Software 』有稍微介紹一下書中 Use Timeouts 這個 stability pattern。今天 Teddy 解了一個 bug,剛好也是和 timeout 有關,順便再幫鄉民們複習一下。
這個 bug 和使用 Java 建立 socket 有關,假設你在寫網路應用程式,有下列幾個步驟:
- client 建立一個 socket 連到 server。
- client 透過 socket 得到一個 output stream,利用這個 output stream 送出 request。
- client 透過 socket 得到一個 input stream,利用這個 input stream 讀取 server 傳回來的 response。
setSoTimeout
public void setSoTimeout(int timeout) throws SocketException
- Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout.
-
- Parameters:
timeout
- the specified timeout, in milliseconds.
如果 ping 一個沒人使用的 IP address,應該會出現如下的結果,也是很快就會有回應。
但是,如果隨便 ping 一個 IP address,整個程式就會卡住,如下圖所示。
如果你的程式是呼叫 public Socket (InetAddress address, int port) 來建立 socket,那麼就可能在個步驟 1 卡住。看一下這個 constractor 的 JavaDoc:
public Socket(InetAddress address, int port) throws IOException
Creates a stream socket and connects it to the specified port number at the specified IP address. If the application has specified a socket factory, that factory's
createSocketImpl
method is called to create the actual socket implementation. Otherwise a "plain" socket is created. 使用這個 constractor 當 Socket 物件被建立的時候,同時也 connect 到遠端。仔細看一下 Socket 物件所提供的 9 個 constractor,沒有一個可以指定 timeout 的,所以程式要改成下面這樣。
- 使用這個 Socket() 不帶參數的 constractor,根據 JavaDoc 的說明這個 constractor『Creates an unconnected socket』。
- 使用 connect (SocketAddress endpoint, int timeout) 來建立連線,根據 JavaDoc 的說明『Connects this socket to the server with a specified timeout value. A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs. 』。
***
如果講到這裡就結束那就遜掉了,還有一個重點是這種 bug 其實不太好找,為什麼?因為同樣的 code 在 Windows 上面並不會有問題,而在 Linux 上面卻會。不要傻傻以為 Java 是跨平台,Java 底層很多程式還是呼叫 native code,網路程式就是一個例子,所以有一些行為還是跟作業系統相關的。總之,跟著 Teddy 大聲念三遍:
寫網路應用程式的時候要記得 Use Timeouts 。
***
友藏內心獨白:如果要害一個人,就送他一張不限航點的飛機艙位升等券...這一句有緣人才看得懂...XD
其實之前在開發WiMAX專案時還遇到更好玩的例子,
回覆刪除由於是用TCP去模擬虛擬的無線網路通道,
ping封包也是用TCP送,
因此會看到2xxx~3xxx ms不等的值出現。
題外話,
回覆刪除Java網路程式我都是用無參數Socket()建立物件後再呼叫connect()...
Hi sprint,
回覆刪除那你平常在呼叫 connect() 的時候會採用有 timeout 的那個版本嗎?
Google '飛機艙位升等券', 會有有趣的現象
回覆刪除通常不會XD
回覆刪除