java網路程式設計——多執行緒資料收發並行
阿新 • • 發佈:2020-03-18
## 基本介紹與思路
#### 收發並行
前一篇部落格中,完成了客戶端與服務端的簡單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