Socket類表現了客戶端套接字,它是屬於一台或兩台計算機的兩個TCP通訊端口之間的通訊通道。端口可以連接到本地系統的另一個端口,這樣可以避免使用另一台計算機,但是大多數網絡軟件將使用兩台計算機。但是TCP套接字不能與兩台以上的計算機通訊。如果需要這種功能,客戶端應用程序必須建立多個套接字連接,每台計算機一個套接字。
構造函數
Java.Net.Socket類有幾個構造函數。其中兩個構造函數允許使用布爾型參數指定是否使用UDP或TCP套接字,我們不贊成使用它們。這兒沒有使用這兩個構造函數,並且沒有列舉在此處--如果需要UDP功能,請使用DatagramSocket。try
{
// 連接到指定的主機和端口
Socket mySocket = new Socket ( "www.awl.com", 80); // ......
}
catch (Exception e)
{
System.err.println ("Err - " + e);
}
但是還有很多構造函數可以用於不同的情形。除非特別指出,所有的構造函數都是公共的。
protected Socket ()-使用當前套接字產生組件提供的默認實現建立不連接的套接字。開發者一般不應該使用這個方法,因為它不允許指定主機名稱和端口。
Socket (InetAddress address, int port)產生 Java.io.IOException異常。
Java.lang.SecurityException-建立連接到指定的IP地址和端口的套接字。如果不能建立連接,或連接到主機違反了安全性約束條件(例如某個小的服務程序試圖連接到某台計算機而不是載入它的計算機時),就產生這種異常。
Socket (InetAddress address, int port, InetAddress localAddress, int localPort)產生java.io.IOException、Java.lang.SecurityException異常-建立連接到指定的地址和端口的套接字,並把它綁定到特定的本地地址和本地端口。默認情況下,使用一個自由(空)的端口,但是在多地址主機環境(例如本地主機有兩個或多個的計算機)中,該方法也允許你指定一個特定的端口號、地址。
protected Socket (SocketImpl implementation)--使用特定的套接字的實現(implementation)建立未連接的套接字。通常情況下開發者不應該使用這個方法,因為它允許指定主機名稱和端口。
Socket (String host, int port)產生Java.Net.UnknownHostException、java.io.IOException、Java.lang.SecurityException異常--建立連接到特定主機和端口的套接字。這個方法允許指定一個字符串而不是一個InetAddress。如果指定的主機名稱不能夠解析,就不能建立連接,如果違反了安全性約束條件就產生異常。
Socket (String host, int port, InetAddress localAddress, int localPort)產生Java.Net.UnknownHostException、java.io.IOException、Java.lang.SecurityException異常--建立連接到特定主機和端口的套接字,並綁定到特定的本地端口和地址。它允許指定字符串形式的主機名稱,而不是指定InetAddress實例,同時它允許指定一個將綁定的本地地址和端口。這些本地參數對於多地址主機(如果可以通過兩個或更多IP地址訪問的計算機)是有用的。如果主機名稱不能解析,就不能建立連接,如果違反了安全性約束條件會產生異常。
1、建立套接字
在正常環境下,建立套接字的時候它就連接了某台計算機和端口。盡管有一個空的構造函數,它不需要主機名稱或端口,但是它是受保護的(protected),在正常的應用程序中不能夠調用它。此外,不存在用於在以後指定這些細節信息的connect()方法,因此在正常的環境下建立套接字的時候就應該連接了。如果網絡是好的,在建立連接的時候,調用套接字構造函數將立即返回,但是如果遠程計算機沒有響應,構造函數方法可能會阻塞一段時間。這是隨著系統的不同而不同的,它依賴於多種因素,例如正在使用的操作系統和默認的網絡超時設置(例如本地局域網中的一些計算機一般比Internet上的計算機響應得快)。你甚至不能肯定套接字將阻塞多長的時間,但是這是非正常的行為,並且它不會頻繁出現。即使如此,在關鍵事務系統中把此類調用放在第二個線程中或許更合適,這樣可以防止應用程序停止。
注意
在較低的層次,套接字是由套接字產生組件(socket factory)產生的,它是一個負責建立適當的套接字實現的特殊的類。在正常環境下,將會產生標准的java.net.Socket,但是在一些特殊的情形中,例如使用自定義套接字的特殊的網絡環境(例如通過使用特殊的代理服務器穿透防火牆),套接字產生組件實際上可能返回一個套接字子類(subclass)。對於錯綜復雜的Java網絡編程比較熟悉,明確為了建立自定義套接字和套接字產生組件的有經驗的開發者可以去了解套接字產生組件的細節信息。對於這個主題的更多信息,你可以查看java.net.SocketFactory和Java.Net.SocketImplFactory類的Java API文檔。
2、使用套接字
套接字可以執行大量的事務,例如讀取信息、發送數據、關閉連接、設置套接字選項等等。此外,下面提供的方法可以獲取套接字的信息(例如地址和端口位置):
方法
void close()產生Java.io.IOException異常--關閉套接字連接。關閉連接可能允許也可能不允許繼續發送剩余的數據,這依賴於SO_LINGER套接字選項的設定。我們建議開發者在關閉套接字連接之前清除所有的輸出流。
InetAddress getInetAddress()--返回連接到套接字的遠程主機的地址。
InputStream getInputStream()產生Java.io.IOException異常--返回一個輸入流,它從該套接字連接到的應用程序讀取信息。
OutputStream getOutputStream()產生Java.io.IOException異常--返回一個輸出流,它向套接字連接到的應用程序寫入信息。
boolean getKeepAlive()產生Java.Net.SocketException異常--返回SO_KEEPALIVE套接字選項的狀態。
InetAddress getLocalAddress()--返回與套接字關聯的本地地址(在多地址計算機中有用)。
int getLocalPort()--返回該套接字綁定在本地計算機上的端口號。
int getPort()--返回套接字連接到的遠程服務的端口號。
int getReceiveBufferSize()產生Java.Net.SocketException異常--返回套接字使用的接收緩沖區大小,由SO_RCVBUF套接字選項的值決定。
int getSendBufferSize()產生Java.Net.SocketException異常--返回套接字使用的發送緩沖區大小,由SO_SNDBUF套接字選項的值決定。
int getSoLinger()產生Java.Net.SocketException異常--返回SO_LINGER套接字選項的值,它控制連接終止的時候未發送的數據將排隊多長時間。
int getSoTimeout()產生Java.Net.SocketException異常--返回SO_TIMEOUT套接字選項的值,它控制讀取操作將阻塞多少毫秒。如果返回值為0,計時器就被禁止了,該線程將無限期阻塞(直到數據可以使用或流被終止)。
boolean getTcpNoDelay()產生Java.Net.SocketException異常--如果TCP_NODELAY套接字選項的設置打開了返回"true",它控制是否允許使用Nagle算法。
void setKeepAlive(boolean onFlag)產生Java.Net.SocketException異常--允許或禁止SO_KEEPALIVE套接字選項。
void setReceiveBufferSize(int size)產生Java.Net.SocketException異常--修改SO_RCVBUF套接字選項的值,它為操作系統的網絡代碼推薦用於接收輸入的數據的緩沖區大小。並不是每種系統都支持這種功能或允許絕對控制這個特性。如果你希望緩沖輸入的數據,我們建議你改用BufferedInputStream或BufferedReader。
void setSendBufferSize(int size)產生Java.Net.SocketException異常--修改SO_SNDBUF套接字選項的值,它為操作系統的網絡代碼推薦用於發送輸入的數據的緩沖區大小。並不是每種系統都支持這種功能或允許絕對控制這個特性。如果你希望緩沖輸入的數據,我們建議你改用BufferedOutputStream或Buffered Writer。
static void setSocketImplFactory (SocketImplFactory factory)產生Java.Net.SocketException、java.io.IOException、Java. lang.SecurityException異常--為JVM指定一個套接字實現的產生組件,它可以已經存在,也可能違反了安全性約束條件,無論是哪種情況都會產生異常。只能指定一個產生組件,當建立套接字的時候都會使用這個產生組件。
void setSoLinger(boolean onFlag, int duration)產生Java.Net. SocketException、Java.lang.IllegalArgumentException異常--激活或禁止SO_LINGER套接字選項(根據布爾型參數onFlag的值),並指定按秒計算的持續時間。如果指定負值,將產生異常。
void setSoTimeout(int duration)產生Java.Net.SocketException異常--修改SO_TIMEOUT套接字選項的值,它控制讀取操作將阻塞多長時間(按毫秒計)。0值會禁止超時設置,引起無限期阻塞。如果發生了超時,當套接字的輸入流上發生讀取操作的時候,會產生Java.io.IOInterruptedException異常。這與內部的TCP計時器是截然不同的,它觸發未知報文包的重新發送過程。
void setTcpNoDelay(boolean onFlag)產生Java.Net.SocketException異常--激活或禁止TCP_NODELAY套接字選項,它決定是否使用Nagle算法。
void shutdownInput()產生Java.io.IOException異常--關閉與套接字關聯的輸入流,並刪除所有發送的更多的信息。對輸入流的進一步的讀取將會遭遇流的結束標識符。
void shutdownOutput()產生java.io.IOException異常--關閉與套接字關聯的輸出流。前面寫入的、但沒有發送的任何信息將被清除,緊接著是TCP連接終止,它通知應用程序沒有更多的數據可以使用了(在Java應用程序中,這樣就到達了流的末尾)。向套接字進一步寫入信息將引起IOException異常。
3、 向TCP套接字讀取和寫入信息
在Java中使用TCP建立用於通訊的客戶端軟件極其簡單,無論使用哪種操作系統都一樣。Java網絡API提供了一致的、平台無關的接口,它允許客戶端應用程序連接到遠程服務。一旦建立了套接字,它就已經連接了並准備使用輸入和輸出流讀取/寫入信息了。這些流都不需要建立,它們是Socket. getInputStream()和Socket.getOutputStream()方法提供的。
為了簡化編程,過濾器可以很容易地連接到套接字流。下面的代碼片斷演示了一個簡單的TCP客戶端,它把BufferedReader連接到套接字輸入流,把PrintStream連接到套接字輸出流。
try
{
// 把套接字連接到某台主機和端口
Socket socket = new Socket ( somehost, someport ); // 連接到被緩沖地讀取程序
BufferedReader reader = new BufferedReader (
new InputStreamReader ( socket.getInputStream() ) );// 連接到打印流
PrintStream pstream =
new PrintStream( socket.getOutputStream() );
}
catch (Exception e)
{
System.err.println ("Error - " + e);
}
4、套接字選項
套接字選項是改變套接字工作方式的設置,並且它們能影響(正反兩方向)應用程序的性能。對於套接字選項的支持是在Java 1.1中引入的,在後面的一些版本中對其中一些做了改進(例如在Java 2 和Java 3中支持SO_KEEPALIVE選項)。通常情況下,不應該修改套接字選項,除非有很必要的原因,因為這種改變可能反面影響應用程序和網絡的性能(例如,激活Nagle算法可能提高telnet類型應用程序的性能,但是會降低可以使用地網絡帶寬)。唯一的例外是SO_TIMEOUT選項--事實上,如果套接字連接的應用程序傳輸數據出現失敗的時候,它都應該溫和地處理超時問題,而不應該因此延遲速度。
⑴SO_KEEPALIVE套接字操作
Keepalive(保持活動)套接字選項是很有爭議的,一些開發者認為使用它會很強大。在默認情況下,兩個連接的套接字之間沒有數據發送,除非應用程序有需要發送的數據。這意味著在長期存活的進程中空閒地的接字可能幾分鐘、幾小時、甚至於幾天不會提交數據。但是,假設某個客戶端崩潰了,並且連接終結序號沒有發送給TCP服務器。貴重的資源(例如CPU時間和內存)將會浪費在哪個永遠不會響應的客戶端上。如果允許keepalive套接字選項,套接字的另一端可以探測以驗證它是否仍然是活動的。但是,應用程序不能控制keepalive探測器的發送頻率。為了激活keepalive,需要調用Socket.setSoKeepAlive(boolean)方法,參數的值為"true"("false"值將禁止它)。例如,為了在某個套接字上允許keepalive,可能使用下面的代碼:
// 激活SO_KEEPALIVE
someSocket.setSoKeepAlive(true);
盡管keepalive的好處並不多,但是很多開發者提倡在更高層次的應用程序代碼中控制超時設置和死的套接字。同時需要記住,keepalive不允許你為探測套接字終點(endpoint)指定一個值。我們建議開發者使用的另一種比keepalive更好的解決方案是修改超時設置套接字選項。
⑵SO_RCVBUF套接字操作
接收緩沖區套接字選項控制用於接收數據的緩沖區。你可以通過調用方法改變它的大小。例如,為了把緩沖區大小改變為4096,可以使用下面的代碼:
// 修改緩沖區大小
someSocket.setReceiveBufferSize(4096);
注意:修改接收緩沖區大小的請求不能保證改變成功。例如,有些操作系統可能不允許修改這個套接字選項,並忽略對該值的任何改變。你可以調用Socket. getReceiveBufferSize()方法得到當前緩沖區的大小。使用緩沖的更好的選擇是使用BufferedInputStream/BufferedReader。
⑶ SO_SNDBUF套接字操作
發送緩沖區套接字選項控制用於發送數據的緩沖區的大小。通過調用Socket.setSendBufferSize(int)方法,你能夠試圖改變緩沖區的大小,但是改變緩沖區大小的請求可能被操作系統拒絕。
// 把發送緩沖區的大小改為4096字節
someSocket.setSendBufferSize(4096);
為了得到當前發送緩沖區的大小,你可以調用Socket.getSendBufferSize()方法,它返回一個整型值。
// 得到默認的大小
int size = someSocket.getSendBufferSize();
使用DatagramSocket類時改變緩沖區大小可能更有效。當對寫進行緩沖的時候,更好的選擇是使用BufferedOutputStream和BufferedWriter。
⑷ SO_LINGER套接字操作
當某個TCP套接字連接被關閉的時候,可能還有一些數據在隊列中等待發送但是還沒有被發送(特別是在IP數據報在傳輸過程中丟失了,必須重新發送的情況下)。Linger(拖延)套接字選項控制未發送的數據可能發送的時間總和,過了這個時間以後數據就會被完全刪除。通過使用Socket.setSoLinger(boolean onFlag, int duration)方法完全激活/禁止linger選項、或者修改linger的持續時間都是可以的。
// 激活linger,持續50秒
someSocket.setSoLinger( true, 50 );
⑸ TCP_NODELAY套接字操作
這個套接字選項是一個標記,它的狀態控制著是否激活Nagle算法(RFC 896)。因為TCP數據是使用IP數據報在網絡上發送的,因此每個包都有一定位數的開銷(例如IP和TCP頭部信息)。如果在某個時刻每個包中只發送了少量的字節,頭部信息的大小將遠遠超過數據的大小。在局域網中,發送的額外的數據可能不會很多,但是在Internet上,成百、成千、甚至於成百萬地客戶端可能通過某個路由器發送這種數據包,加起來顯著地增加了帶寬的消耗。
3、 向TCP套接字讀取和寫入信息
在Java中使用TCP建立用於通訊的客戶端軟件極其簡單,無論使用哪種操作系統都一樣。Java網絡API提供了一致的、平台無關的接口,它允許客戶端應用程序連接到遠程服務。一旦建立了套接字,它就已經連接了並准備使用輸入和輸出流讀取/寫入信息了。這些流都不需要建立,它們是Socket. getInputStream()和Socket.getOutputStream()方法提供的。
為了簡化編程,過濾器可以很容易地連接到套接字流。下面的代碼片斷演示了一個簡單的TCP客戶端,它把BufferedReader連接到套接字輸入流,把PrintStream連接到套接字輸出流。
try
{
// 把套接字連接到某台主機和端口
Socket socket = new Socket ( somehost, someport ); // 連接到被緩沖地讀取程序
BufferedReader reader = new BufferedReader (
new InputStreamReader ( socket.getInputStream() ) );// 連接到打印流
PrintStream pstream =
new PrintStream( socket.getOutputStream() );
}
catch (Exception e)
{
System.err.println ("Error - " + e);
}
4、套接字選項
套接字選項是改變套接字工作方式的設置,並且它們能影響(正反兩方向)應用程序的性能。對於套接字選項的支持是在Java 1.1中引入的,在後面的一些版本中對其中一些做了改進(例如在Java 2 和Java 3中支持SO_KEEPALIVE選項)。通常情況下,不應該修改套接字選項,除非有很必要的原因,因為這種改變可能反面影響應用程序和網絡的性能(例如,激活Nagle算法可能提高telnet類型應用程序的性能,但是會降低可以使用地網絡帶寬)。唯一的例外是SO_TIMEOUT選項--事實上,如果套接字連接的應用程序傳輸數據出現失敗的時候,它都應該溫和地處理超時問題,而不應該因此延遲速度。