基于Java的TCP Socket通信详解
TCP Socket通信是一種比較常用的基于連接的網絡通信方式。本文通過Java實現TCP Socket通信,并將其用于計算機端,Android手機端,硬件設備端,同時做到代碼規范化,實現代碼最大化復用。
| 本文代碼可在GitHub下載,建議對照源碼閱讀文章 https://github.com/IVanMissAya/tcp_server
TCP連接的建立
客戶端和服務器間通過 三次握手 建立TCP連接。在Java中,連接建立完成后,服務器端和客戶端分別獲取到一個Socket實例,之后就可以通過這個Socket實例進行通信。服務器端和客戶端使用不同的方法獲取Socket實例。
服務器端
在服務器端,通過ServerSocket實現對指定端口的監聽,代碼如下。其中port為int型端口數值,取值065535,01024為系統保留端口,這里取值1234。如果發生錯誤將會拋出異常。
int port = 1234; ServerSocket server = new ServerSocket(port);通過ServerSocket.accept()方法接受客戶端連接。這個方法是阻塞的,從調用時開始監聽端口,直到客戶端連接建立時,執行結束并返回Socket實例。連接建立失敗會拋出異常。
Socket socket = server.accept();客戶端
客戶端直接通過實例化的形式,產生Socket實例。實例化的過程中,嘗試連接指定的服務器主機。連接成功則實例化完成,連接失敗則拋出異常。hostIP為主機的IP地址,port為端口號,和服務器主機監聽的端口號保持一致。
String hostIP = "127.0.0.1"; int port = 1234; Socket socket = new Socket(hostIP, port);連接的建立過程
以上代碼的執行順序是:
- 服務器端實例化ServerSocket:new ServerSocket(port);
- 服務器端執行accept(),監聽指定端口,此方法阻塞等待客戶端連接:server.accept();
- 客戶端實例化Socket實例,嘗試連接服務器:new Socket(hostIP, port);
- TCP三次握手成功,服務器端的accept()返回Socket實例,同時客戶端的Socket實例化成功。
Socket的讀寫
以收發字符串為例來說明Socket的讀寫。
向Socket對象寫入數據,則會發送至TCP連接的另一方。這個操作在服務器端和客戶端是一樣的。可通過獲取Socket的輸出流來寫入UTF8格式編碼的字符串,代碼如下。寫入完成后,就會被發送到連接的另一端。
private DataOutputStream out; out = new DataOutputStream(socket.getOutputStream()); String s = "Test"; out.writeUTF(s); out.flush();在接收端,通過獲取Socket的輸入流,就可以讀取字符串數據,代碼如下。readUTF()方法是阻塞的,直到對方發送完一個字符串,該方法才會執行結束并返回收到的字符串。如果連接中斷,或強制關閉Socket的輸入流,即執行socket.shutdownInput(),該方法會拋出異常。
private DataInputStream in; in = new DataInputStream(socket.getInputStream()); String s = in.readUTF();在建立了TCP連接后,由于無法確定對方的數據發送時間,為了保證及時接收到數據,通過一個新線程不斷調用in.readUTF()方法讀取數據(相當于輪詢法);并在接收到數據后回調相關函數,對數據進行處理。
TCP連接的斷開
TCP Socket連接是雙向的,通過 四次揮手 的方式斷開,雙方分別調用Socket.close()方法斷開連接。連接斷開的過程中,一般一方A先斷開連接,另一方B發現A斷開連接后,也斷開連接。為方便表述,將先斷開連接的一方A稱為“主動斷開連接”;后斷開的一方B,則為“被動斷開連接”。
在一方B阻塞執行in.readUTF()方法時,如果對方A主動斷開Socket連接,這個方法會拋出異常。從而在B處理異常時,可以被動的斷開這邊的連接。
為保證主動斷開連接的一方不會阻塞在in.readUTF()方法中,需要先執行socket.shutdownInput()。所以主動斷開連接的代碼如下。
socket.shutdownInput(); in.close(); socket.close();被動斷開連接的一方,在捕獲到in.readUTF()的異常后,斷開Socket連接。
try {String s = in.readUTF(); } catch (IOException e) {// 連接被斷開(被動)try {in.close();socket.close();in = null;socket = null;} catch (IOException e) {e.printStackTrace();} }SocketTransceiver的實現
考慮到在服務器端和客戶端,Socket對象的操作是完全一樣的,所以實現了一個SocketTransceiver(收發器),實現對Socket的直接操作,其他代碼則通過SocketTransceiver間接操作Socket對象實現數據收發、斷開連接等。
SocketTransceiver實現的功能有:
- 開啟新線程不斷查詢Socket是否收到數據;
- 將字符串、文件等類型的數據進行打包,并通過Socket發送;
- 從Socket接收數據,并自動解析出數據(字符串、文件等),接收完成后回調相應的方法;
- 在發生錯誤、連接被動斷開時,自動斷開連接并進行相關處理,并回調相應方法。
SocketTransceiver.class 使用抽象類實現,回調方法是抽象的,實例化時對抽象方法進行實現,處理回調。完整代碼見附件。
TcpServer的實現
TcpServer為TCP Socket服務器端程序。為了讓服務器能同時接受并處理來自多個客戶端的TCP連接請求:
- TcpServer中用一個監聽線程對端口進行監聽,即阻塞執行server.accept()方法,等待接受客戶端連接;
- 服務器端每次與一個客戶端建立連接,即accept()方法執行結束并返回一個Socket對象,就會用一個SocketTransceiver對這個Socket進行操作;
- 連接建立后,監聽線程再次執行server.accept()方法,繼續監聽端口并等待下一個連接;
- 服務器端有一個List,保存當前連接的每個客戶端對應的SocketTransceiver對象,在需要時可取出并進行操作。
TcpClient的實現
TcpClient為TCP Socket客戶端程序。主要工作是進行Socket的連接,并利用SocketTransceiver對Socket進行操作。
另附 windows 下tcp 測試工具
sokit 是一款開源免費的 TCP / UDP 測試(調試)工具, 可以用來接收,發送或轉發TCP/UDP數據包。 下載地址: http://sqdownd.onlinedown.net/down/sokit-1.3-win32-chs.zip
TCPUDP測試工具用于開發網絡通訊程序時,在服務器或客戶端測試TCP/UDP通訊連接和測試數據的接收和發送情況。 下載地址: http://fastsoft.onlinedown.net/down/TCPUDPDebug102_Setup.exe
總結
以上是生活随笔為你收集整理的基于Java的TCP Socket通信详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WebView退出时停止视频播放
- 下一篇: 对Java注解(Annotation)初