1. 程式人生 > >java網路程式設計——多執行緒資料收發並行

java網路程式設計——多執行緒資料收發並行

## 基本介紹與思路 #### 收發並行 前一篇部落格中,完成了客戶端與服務端的簡單TCP互動,但這種互動是觸發式的:客戶端傳送一條訊息,服務端收到後再回送一條。沒有做到收發並行。收發並行的字面意思很容易理解,即資料的傳送與接收互相不干擾,相互獨立。當然,要保證服務端和客戶端都能做到收發並行。 #### 業務邏輯 脫離業務邏輯的實踐是毫無意義的,先描述一下本實踐中的業務邏輯:一個服務端接受多個客戶端的連線,連線後,向各個客戶端定時傳送時間戳資料,同時在並行條件下,接受各個客戶端傳送來的資料並顯示;客戶端鍵盤輸入字串,傳送給服務端,同時在並行條件下,接收伺服器發來的時間戳資料並顯示。 #### 實現思路 實現傳送與接收並行,思路其實非常直觀,即建立兩個執行緒,分別用來實現輸入流和輸出流。我的程式碼的設計方案如下圖所示: ![image](http://qiniu.debrisflow.cn/20200317diagram.png) - 服務端:建立一個監聽客戶端連線的執行緒,執行緒中一旦接收到請求,建立一個對應該客戶端收發處理的物件,物件中建立輸入流執行緒,並使用單例執行緒池建立輸出流執行緒。主執行緒使用鍵盤輸入流System.in來進行阻塞。同時主執行緒中建立Timer定時器,定時向輸出流傳送資料。 - 客戶端:主執行緒傳送連線請求,與伺服器建立連線。使用鍵盤輸入流System.in來阻塞主執行緒,同時作為輸出流使用;建立一個輸入流執行緒,非同步執行,接收伺服器資料。 ## 程式碼分析 原始碼檔案結構如下圖所示 ![image](http://qiniu.debrisflow.cn/20200317projectStructure.jpg) #### 服務端 伺服器端分為三個部分,分別是Server.java,TCPServer.java和ClientHandler.java ###### Server.java ``` package Server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.TimerTask; import java.util.Timer; import java.util.Date; public class Server { private static SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd_HH:mm:ss"); public static void main(String[] args){ try { TCPServer.accept(); new Timer("Timer").schedule(new TimerTask() { @Override public void run() { TCPServer.broadcast(df.format(new Date())); } }, 1000,5000); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String str; //因為ClientListen是非同步執行緒,使用鍵盤輸入流將主執行緒阻塞住,保證跟ClientListen執行緒同步,同時可控制ClientListen服務的退出 do{ str = bufferedReader.readLine(); }while (str.equalsIgnoreCase("serverExit")); }catch (Exception e){ System.out.println("監聽請求過程中異常退出"); } try { TCPServer.stop(); } catch (IOException e) { System.out.println("關閉套接字過程中出現異常"); } finally { System.out.println("伺服器端套接字已關閉!"); } } } ``` ###### TCPServer.java ``` package Server; import java.io.IOException; import java.net.*; import java.util.ArrayList; import java.util.UUID; class TCPServer { private static int LOCAL_PORT = 3001; private static ClientListenHandle clientListenHandle; private static A