控制預設行為 TCP/IP 協議的開發人員用了大量的時間來考慮協議的預設行為,以滿足大部分應用程式的需要。(如果你對此表示懷疑,可以參考RFC1122 和1123,它們根據多年的經驗對TCP/IP協議的實現的推薦行為進行了詳盡的描述。)對於大多數應用程式來說,這些設計都非常適合,然而,"滿足所有需求的一種尺寸"能夠真正適用於所有場合的情況非常少。前面我們已經看到UDP 回顯用戶端的例子。預設情況下,DatagramSocket 類的receive()方法將無限期地阻塞等待一個資料報文,在我們的例子中,通過在UDP 通訊端上指定了一個接收逾時時間並在TimeLimitEchoProtocol 類中使用了setSoTimeout()進行設定,從而改變了協議的預設行為。 1 Keep-Alive 如果一段時間內沒有資料交換,通訊的每個終端可能都會懷疑對方是否還處於活躍狀態。TCP 協議提供了一種keep-alive 的機制,該機制在經過一段不啟用時間後,將向另一個終端發送一個探測訊息。如果另一個終端還出於活躍狀態,它將回複一個確認訊息。如果經過幾次嘗試後依然沒有收到另一終端的確認訊息,則終止發送探測資訊,關閉通訊端,並在下一次嘗試I/O 操作時拋出一個異常。注意,應用程式只要在探測資訊失敗時才能察覺到keep-alive 機制的工作。 Socket: 保持活躍 boolean getKeepAlive() void setKeepAlive(boolean on) 預設情況下,keep-alive 機制是關閉的。通過調用setKeepAlive()方法將其設定為true 來開啟keep-alive 機制。 2發送和接收緩衝區的大小 一旦建立了一個Socket 或DatagramSocket 執行個體,作業系統就必須為其分配緩衝區以存放接收的和要發送的資料。(我們將在第6.1 節對其進行更詳細的介紹) Socket, DatagramSocket: 設定和擷取發送接收緩衝區大小 int getReceiveBufferSize() void setReceiveBufferSize(int size) int getSendBufferSize() void setSendBufferSize(int size) getReceiveBufferSize(),setReceiveBufferSize(),getSendBufferSize(),和setSendBufferSize() 方法分別用於擷取和設定接收發送緩衝區的大小(以位元組為單位)。需要注意的是,這裡指定的大小隻是作為一種建議給出的,實際大小可能與之存在差異。 還可以在ServerSocket 上指定接收緩衝區大小。不過,這實際上是為accept()方法所建立的新Socket 執行個體設定接收緩衝區大小。為什麼可以只設定接收緩衝區大小而不設定發送緩衝區的大小呢?當接收了一個新的Socket,它就可以立刻開始接收資料,因此需要在accept()方法完成串連之前設定好緩衝區的大小。另一方面,由於可以控制什麼時候在新接受的通訊端上發送資料,因此在發送之前還有時間設定發送緩衝區的大小。 ServerSocket: 設定/擷取所接受通訊端的接收緩衝區大小 int getReceiveBufferSize() void setReceiveBufferSize(int size) getReceiveBufferSize()和setReceiveBufferSize()方法用於擷取和設定由accept()方法建立 的Socket 執行個體的接收緩衝區的大小(位元組)。 3逾時 如前面所介紹的,很多I/O 操作如果不能立即完成就會阻塞等待:讀操作將阻塞等待直到至少有一個位元組可讀;接收操作將阻塞等待直到成功建立串連。不幸的是阻塞的時間沒限制。可以為各種操作指定一個最大阻塞時間。 Socket, ServerSocket, DatagramSocket: 設定/擷取I/O 逾時時間 int getSoTimeout() void setSoTimeout(int timeout) getSoTimeout()和setSoTimeout()方法分別用於擷取和設定讀/接收資料操作以及accept操作的最長阻塞時間。逾時設定為0 表示該操作永不逾時。如果阻塞超過了逾時時間長度,則拋出一個異常。 4地址重用 在某些情況下,可能希望能夠將多個通訊端綁定到同一個通訊端地址。對於UDP 多播的情況,在同一個主機上可能有多個應用程式加入了相同的多播組。對於TCP,當一個串連關閉後,通訊的一端(或兩端)必須在"Time-Wait"狀態上等待一段時間,以對傳輸途中丟失的資料包進行清理(見第6.4.2 節)。不幸的是,通訊終端可能無法等到Time-Wait 結束。對於這兩種情況,都需要能夠與正在使用的地址進行綁定的能力,這就要求實現地址重用。 Socket, ServerSocket, DatagramSocket: 設定/擷取地址重用 boolean getReuseAddress() void setReuseAddress(boolean on) getReuseAddress()和setReuseAddress()方法用於擷取和設定地址重用許可。設定為true表示啟用了地址重用功能。 5消除緩衝延遲 TCP 協議將資料緩衝起來直到足夠多時一次發送,以避免發送過小的資料包而浪費網路資源。雖然這個功能有利於網路,但應用程式可能對所造成的緩衝延遲不能容忍。好在可以人為禁用緩衝功能。 Socket: 設定/擷取TCP 緩衝延遲 boolean getTcpNoDelay() void setTcpNoDelay(boolean on) getTcpNoDelay()和setTcpNoDelay()方法用於擷取和設定是否消除緩衝延遲。將值設定 為true 表示禁用緩衝延遲功能。 6緊急資料 假設你已經向一個慢速接收者發送了很多資料,突然又有了它急需的其它資料。如果將這些資料發送到輸出資料流,它們將追加在常規資料隊列的後面,無法保證接收者能夠立即接收。為瞭解決這個問題,TCP 協議中包含了緊急(urgent)資料的概念,這類資料可以(理論上來說)跳到前面去。由於它們能夠繞過一般資料流,這些資料稱為頻道外資料。 Socket: 緊急資料 void sendUrgentData(int data) boolean getOOBInline() void setOOBInline(boolean on) 要發送緊急資料需要調用sendUrgentData() 方法,它將發送其int 參數的最低位位元組。要接收這個位元組,必須為setOOBInline()方法傳遞true 參數啟用接收者對頻道外資料的接收。該位元組在接收者的輸入資料流中被接收。發送於緊急位元組之前的資料將處於接收者的輸入資料流中的緊急位元組前面。如果沒有啟用接收者接收頻道外資料的功能,緊急位元組將被無聲地丟棄。 注意Java 中的緊急資料幾乎沒什麼用,因為緊急位元組與常規位元組按照傳輸的順序混在了一起。實際上,Java 接收者並不能區分其是否在接收緊急資料。 7關閉後停留 當調用通訊端的close()方法後,即使通訊端的緩衝區中還有沒有發送的資料,它也將立即返回。這樣不發送完所有資料可能導致的問題是主機將在後面的某個時刻發生故障。其實可以選擇讓close()方法"停留"或阻塞一段時間,直到所有資料都已經發送並確認,或發生了逾時。詳情見第6.4.2 節 Socket: 在close()方法停留 int getSoLinger() void setSoLinger(boolean on, int linger) 如果調用setSoLinger()並將其設定為true,那麼後面再調用的close()方法將阻塞等待,直到遠程終端對所有資料都返回了確認資訊,或者發生了指定的逾時(秒)。如果發生了逾時,TCP 串連將強行關閉。如果開啟了停留功能,getSoLinger()方法將返回指定的逾時時間,否則返回-1。 8廣播許可 一些作業系統要求顯示地對廣播許可進行請求。你可以對廣播許可進行控制。正如前面所介紹的,DatagramSockets 類提供了廣播服務功能。 DatagramSocket: 設定/擷取廣播許可 boolean getBroadcast() void setBroadcast(boolean on) getBroadcast()和setBroadcast()方法分別用於擷取和設定廣播許可。設定為true 表示允許廣播。在Java 中,預設情況下是允許進行廣播的。 9通訊等級 有的網路對滿足服務條件的資料包提供了增強服務或"額外的保險"。一個資料包的通訊等級(traffic class)由資料包在網路中傳輸時其內部的一個值來指定。例如,有的網路會為"黃金服務"等級的資料包提供較高的優先順序,以減少其傳輸延遲和丟包的機率。有的網路可能會根據指定的通訊等級為資料包選擇路由。不過需要注意的是,網路的提供者對這類服務要收取額外的費用,因此不能保證這些選項實際上是否生效。 Socket, DatagramSocket: 設定/擷取通訊等級 int getTrafficClass() void setTrafficClass(int tc) 通訊等級通常由一個整數或一組位標記指定。位標記的數量和意義則取決於所使用的IP 協議版本。 10 基於效能的協議選擇 TCP 協議並不是通訊端惟一可選的協議。使用什麼樣的協議取決於應用程式的側重點在是什麼。 Java 允許開發人員根據不同效能特徵對於應用程式的重要程度,為具體實現給出"建議"。底層的網路系統可能會根據這些建議,在一組能夠提供同等的資料流服務,同時又具有不同的效能特徵的不同協議中做出選擇。 Socket, ServerSocket: 指定協議參數選擇 void setPerformancePreferences(int connectionTime, int latency, int bandwidth) 通訊端的績效參數由三個整數表示,分別代表連線時間,延遲和頻寬。具體的數值並不重要,Java 將比較各種標準的相關參數值,並返回與之最匹配的可用協議。例如,如果connectionTime 和latency 都等於0,bandwidth 等於1,那麼則將選擇能夠使頻寬最大的協議。注意,要使這個方法生效,必須在通訊端建立串連之前調用。