Java網路程式設計——Socket通訊
- Socket通訊基於TCP/IP協議。TCP/IP通訊協議是一種可靠的網路協議,它在通訊的兩端各建立一個Socket,從而在通訊的兩端之間形成網路虛擬鏈路。一旦建立了虛擬的網路鏈路,兩端的程式就可以通過網路虛擬鏈路進行通訊。
- Java對基於TCP/IP協議的網路通訊提供了良好的封裝,Java使用Socket物件來代表兩端的通訊埠,並通過Socket產生IO流來進行網路通訊。
TCP/IP協議基礎介紹
1. IP協議
- IP協議是Internet上使用的一個關鍵協議,全稱是Internet Protocol,即Internet協議,簡稱IP協議。
- 通過使用IP協議,從而使Internet成為一個允許連線不同型別的計算機和不同作業系統的網路。
- IP協議保證計算機能傳送和接收分組資料。IP協議負責將訊息從一個主機傳送到另一個主機,訊息在傳送過程中被分割成一個個的小包。
IP協議只是保證了計算機之間可以傳送和接收資料,但IP協議並不能解決資料分組在傳輸過程中可能出現的異常情況。此時需要通過TCP協議來提供可靠並且無差錯的通訊服務。
2. TCP協議
- TCP協議被稱作一種端對端協議。當一臺計算機需要與另一臺遠端計算機連線時,TCP協議會讓它們建立一個連線,即用於傳送和接收資料的虛擬鏈路。
- TCP協議負責收集資料包,並將它們按適當的次序傳送,接收端收到後再將它們正確地還原。
- TCP協議使用重發機制: 當一個通訊實體傳送一個訊息給另一個通訊實體後,需收到另一個通訊實體的確認資訊,如果沒有收到另一個通訊實體的確認資訊,則會再從發剛才傳送的資訊。
- 通過TCP協議的重發機制,TCP協議嚮應用程式提供了可靠的通訊連線,使它能夠自動適應網上的各種變化,即使在Internet暫時出現堵塞的情況下,TCP也能保證通訊的可靠性。
雖然IP和TCP這兩個協議功能不盡相同,也可以分開單獨使用,但它們是在同一個時期作為一個協議來設計的,並且在功能上也是互補的。只有兩者結合起來,才能保證Internet在複雜的環境下正常執行。凡是要連線到Internet的計算機,都必須同時安裝和使用這兩個協議,因此實際使用中常常把這兩個協議統稱為TCP/IP協議。
Java中Socket通訊相關的API介紹
一、ServerSocket
- 在兩個通訊實體沒有建立虛擬鏈路之前,必須有一個通訊實體先做出“主動姿態”,主動接收來自其他通訊實體的連線請求。可以將此做出“主動姿態”的通訊實體看做是TCP伺服器端。
- Java中能接收其他通訊實體連線請求的類是ServerSocket。ServerSocket物件用於監聽來自客戶端的Socket連線,如果沒有連線,ServerSocket物件將一直處於等待狀態。
public ServerSocket(int port)
:使用指定的埠port來建立ServerSocket。為了避免與其他應用程式的通用埠衝突,建議使用1024以上的埠。
public ServerSocket(int port, int backlog)
: 指定連線佇列中最大能允許多少個來自客戶端的Socket連線請求。沒有指定backlog或backlog小於1,則使用預設值(Java1.7的預設值是50)。
public ServerSocket(int port, int backlog, InetAddress bindAddr)
: 在機器存在多個IP地址的情況下,允許通過localAddr引數來指定將ServerSocket繫結到指定的IP地址。
public Socket accept()
: 監聽來自客戶端的連線請求。如果接收到一個客戶端Socket的連線請求,該方法將返回一個與客戶端Socket對應的Socket;否則該方法將一直處於等待狀態,執行緒也被阻塞。當呼叫此accept方法返回一個Socket物件後,通訊的兩端之間就形成網路虛擬鏈路,於是就可以通過兩端的Socket物件產生的IO流,經過網路虛擬鏈路進行通訊了。
public void close()
: 當ServerSocket使用完畢後,通過該close方法來關閉ServerSocket。呼叫close方法後,accept中阻塞的執行緒將丟擲SocketException異常:throw new SocketException("Socket is closed");
。並且與客戶端建立的Socket連線也會被關閉。
通常情況下,伺服器不會只接受一個客戶端請求,而應該不斷地接收來自客戶端的所有請求,所以Java程式通常會通過迴圈(比如一個死迴圈)不斷地呼叫ServerSocket的accept()方法。
二、Socket
客戶端通過建立一個Socket物件,連線到伺服器。
public Socket(String host, int port)
public Socket(InetAddress address, int port)
- 使用本地主機預設的IP地址和系統動態分配的埠, 建立一個Socket,並使該Socket物件連線到指定遠端主機和遠端埠的伺服器程式(連線成功後會通過伺服器端ServerSocket的accept()方法,在伺服器端返回一個與此客戶端Socket對應的伺服器端Socket物件)。
- 遠端主機的IP地址可以使用字串形式的host表示,也可以封裝成一個InetAddress物件傳遞進來。
public Socket(String host, int port, InetAddress localAddr, int localPort)
public Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
- 使用指定的本地IP地址localAddress,和指定的本地埠localPort,建立一個Socket,並使該Socket物件連線到指定遠端主機和遠端埠的伺服器程式(連線成功後會通過伺服器端ServerSocket的accept()方法,在伺服器端返回一個與此客戶端Socket對應的伺服器端Socket物件)。
- 遠端主機的IP地址可以使用字串形式的host表示,也可以封裝成一個InetAddress物件傳遞進來。
public Socket()
: 建立一個未連線的Socket物件。如果想將其與伺服器端的Socket連線, 還需調connect方法。
public void connect(SocketAddress endpoint, int timeout)
: 連線到endpoint指定的遠端伺服器端的Socket程式。設定timeout指定連線超時時間,單位ms。timeou設定為0表示沒有限制超時時間。
- 通過SocketAddress的子類InetSocketAddress來封裝遠端主機和遠端埠,相關構造方法是
InetSocketAddress(String hostname, int port)
或InetSocketAddress(InetAddress addr, int port)
public synchronized void setSoTimeout(int timeout)
: 從Socket中讀取輸入流設定的超時時間,單位是ms。
public InputStream getInputStream()
: 返回該Socket物件對應的輸入流,讓程式通過該輸入流從遠端Socket中取出資料。
public OutputStream getOutputStream()
: 返回該Socket物件對應的輸出流,讓程式通過該輸出流向遠端Socket中寫入資料。
- API只是返回輸入/輸出節點流的基類,所以不管底層的IO流到底是什麼節點流,程式都可以將其包裝成處理流,從而提供更多方便的處理。
public synchronized void close()
: 關閉該Socket物件,
- Socket中,當前操作IO流的阻塞執行緒將會報SocketException異常;
- 呼叫此close方法後的Socket物件,無法再進行連線。若還需連線,則要另外建立一個新的Socket物件;
- 呼叫此close方法,Socket中的InputStream和OutputStream都會被關閉。
public boolean isClosed()
: 判斷此Socket物件是否調了close方法做了關閉處理。
- 如果只是調shutdownInput和/或shutdownOutput關閉了輸入/輸出流,而沒有調close方法關閉Socket,isClosed仍然返回true。
public void shutdownInput()
: 關閉該Socket物件的輸入流,程式還可以通過該Socket的輸出流輸出資料。
public void shutdownOutput()
: 關閉該Socket物件的輸出流,程式還可以通過該Socket的輸入流讀取資料。
public boolean isInputShutdown()
: 判斷Socket物件的輸入流是否關閉。
public boolean isOutputShutdown()
: 判斷Socket物件的輸出流是否關閉。
- 即使對一個Socket物件先後呼叫shutdownInput和shutdownOutput,該Socket物件仍然沒有被關閉,只不過此時的Socket物件既不能輸入資料,也不能輸出資料。
- 當呼叫了Socket物件的shutdownInput或shutdownOutput關閉輸入流或輸出流後,該Socket物件無法再次開啟輸入流或輸出流。因此這種做法不適合保持持久通訊狀態的互動式應用。
使用Java提供的Socket通訊相關API實現多人聊天功能
//TODO (加入多執行緒、在通訊資料中加入協議字元區分資料資訊、相關原始碼) 未完待續…