2016/06/07

Socket Options

開發網路程式的朋友都應該用過socket,而socket中除了基本的方法與屬性外,還有不少options。如果觀察Socket的JavaDoc,你一定會發現有很多關於SO_XXXX、IP_XXXX、TCP_XXXX之類的屬性。這些屬性多半與通訊的連線成功或傳輸關係不是那麼大。但是,如果程式需要關注到效能,那這些屬性就會扮演重要的角色。

從Java的角度來檢視一下這些options:


IP_TOS


TOS(Type-Of-Service)的值必須介於[0..255]間。RFC 1394中則定義了下列

  • 0x00 -- normal service
  • 0x02 -- minimize monetary cost
  • 0x04 -- maximize reliability
  • 0x08 -- maximize thrughput
  • 0x10 -- minimize delay

程式可依需要將這些值做bitwise運算,來設定通訊時的等級(例如0x14是指minimize delay and maximize reliability)。
不過並不是所有的設備都會依照這個option的設定來進行傳輸,但是有需要還是可以設上。

SO_LINGER

linger是拖延的意思,這會影響Socket.close()的速度。
如果觀察TCP State Diagram,當我們做close socket的動作時,連線兩端還是會做許多複雜的動作。其中active close這端在TIME_WAIT會等待 2*MSL (Maximum Segment Lifetime)的時間,來確保送出的資料可以被另一端接收到。在RFC 793中提到了MSL是120秒。不同的OS與應用會去調整這個值,目前多數的Linux、BSD及Windows是30秒。

對於寫server-side程式的人來說,linger時間太長,你的程式雖然已經close這個socket了,但是在底層並沒有真的關掉,還在TIME_WAIT的狀態,造成了程式在短時間內沒有辦法再bind到這個port。
現在的internet速度與穩定性已經比早年好太多了,應該不需要這麼長的linger time了。如果你的程式需要較多的socket開關操作時,可以考慮把linger time設小一點。

如果把linger time設為0呢?則程式會送出設有RST flag的封包給對方,可以達到快速結束連線的效果。但是,對方的程式可能會發生 "Connection reset by peer" 的錯誤。

SO_TIMEOUT

這個時間是Socket.getInputStream().read()會被block的等待時間長度。如果read等待超過這個時間長度,則會丟出java.net.SocketTimeoutException。
SO_TIMEOUT的值須大於0,若為0,則視為無止盡。
這個值設太小,就容易常發生timeout的現象;若設太大,有時程式又會被block太久,反應速度變長。所以如何拿捏就要看應用類型而定了。

SO_SNDBUF / SO_RCVBUF

對底層的傳送/接收緩衝區大小的建議值。
這只是建議值,底層會依照所設定的值,在參照實際系統狀況來決定這兩個緩衝區的大小。
前輩建議,如果是在通訊較不好的環境,或是瞬間傳送的速度很快,那SO_SNDBUF可以設大些,這樣可以確保TCP sliding window夠大,足以容納一值無法成功送出的資料。
如果是電腦的處理速度慢,那SO_RCVBUF就要設大些,免得來不及處理,資料掉了。

SO_KEEPALIVE

如果socket建立了,但是長時間(2小時,視OS實作而定)沒有進行資料傳送,這個連線很可能會被中間的某個網路設備中止。為了避免這情形發生,如果啟用了SO_KEEPALIVE,則socket會自動發出一個keepalive probe給對方,對方也會回應回來,讓網路設備知道這個連線仍在使用中。
對有些應用或環境來說,2小時真的太長了(尤其是行動網路,有些系統商大概不會讓你站用連線這麼久),而且對Java也無法變更改時間值,所以許多應用就會自己實作ping/pong的機制來當作keep-alive。

SO_OOBINLINE

當連線有許多資料排隊要送時,如果有一緊急事件要插隊通知對方,就可以用 OOB (Out-Of-Band) Inline。啟用SO_OOBINLINE,並可透過Socket.sendUrgentData來即時送出 1-byte的資料給對方。
不過,直到Java 8,都沒有看到可以接收urgent data的方法。所以...

TCP_NODELAY

在提這項之前,要先瞭解Nagle's algorithm。簡單的說,想想一家貨運公司,如果一收到小貨物就馬上出車送貨,對公司來說整個營運效能就不高,對整個道路來說就會增加車次,容易造成路上塞車。
若是貨運公司將這些收到的小貨物累積到一定的量才出車送貨,那公司的營運效能才能提高,不會造成浪費。但是這就會造成最前面的幾件貨物要等很久才會被送出。
TCP_NODELAY就是停用Nagle's algorithm,來提高throughput。
如果所開發的系統常會傳送小資料,這個選項就該納入考慮。


以上做為工作上的紀錄,方便未來查閱。也希望對大家都有些幫助。