【Java】Java socket通信使用read,readline函數的阻塞問題

Socket通信是Java網絡編程中比較基礎的部分,其原理其實就是源ip,源端口和目的ip,目的端口組成的套接字通信。其底層還設及到了TCP協議的通信。

Java中的Socket通信可以通過客戶端的Socket與服務端的ServerSocket通信,同時利用IO流傳遞數據,也就是說Socket通信是面向流的使用的是BIO,並不同於後來的NIO通信面向緩衝。Socket通信中使用的IO流的read,readline等函數都是阻塞的,這就導致了在通信過程中,雙方不能確定什麼時侯是流的結束,針對這種可以通過約定結束符的方式進行結束,也可以約定一次傳輸的位元組流的長度。下面通過代碼進行說明

客戶端

建立客戶端線程,在run方法中不斷對服務端進行發送消息,模擬多個客戶端的通信,通過寫入換行符,表明這次通信的結束。

 1 class Client implements Runnable {
 2 
 3     private byte[] targetIp;
 4     private int port;
 5 
 6     Client(byte[] ip, int port) {
 7         this.targetIp = ip;
 8         this.port = port;
 9     }
10 
11     @Override
12     public void run() {
13         try {
14             InetAddress inetAddress = InetAddress.getByAddress(targetIp);
15             Socket socket = new Socket(inetAddress, port);
16             System.out.println("client");
17             BufferedReader socketInput = new BufferedReader(new InputStreamReader(socket.getInputStream()));
18             BufferedWriter socketOutput = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
19 
20             int i = 0;
21             String NAME = "Client";
22             while (true) {
23                 socketOutput.write("This msg from " + NAME + " msg id is " + i);
24                 socketOutput.write("\n");//約定結束符表示流的結束
25                 i++;
26                 socketOutput.flush();
27                 System.out.println("here");
28                 String str = null;
29                 if (!(str = socketInput.readLine()).equals("\n")) {
30                     System.out.println(str);
31                 }
32 
33             }
34 
35             /*socket.shutdownInput();
36             socket.shutdownOutput();*/
37         } catch (IOException e) {
38             e.printStackTrace();
39         }
40 
41     }
42 }

服務端

服務端通過accept接受客戶端的連接,這個操作是阻塞的,直到有客戶端的連接才能進行下一步。

 1 class Server implements Runnable {
 2 
 3     private int port;
 4 
 5     Server(int port) {
 6         this.port = port;
 7     }
 8     @Override
 9     public void run() {
10         try {
11             ServerSocket serverSocket = new ServerSocket(port);
12 
13             InetAddress inetAddress = serverSocket.getInetAddress();
14             System.out.println("server" + inetAddress.getHostAddress());
15             Socket socket = serverSocket.accept();
16             BufferedReader socketInput = new BufferedReader(new InputStreamReader(socket.getInputStream()));
17             BufferedWriter socketOutput = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
18 
19             int i = 0;
20             while (true) {
21                 String str = null;
22                 if (!(str = socketInput.readLine()).equals("\n")) System.out.println(str);
23                 System.out.println("server");
24 
25                 String NAME = "Server";
26                 socketOutput.write("This msg from " + NAME + " msg num is " + i + " reply to " + str);
27                 socketOutput.write("\n");
28                 i++;
29                 socketOutput.flush();
30             }
31 
32 //            socket.shutdownInput();
33 //            socket.shutdownOutput();
34         } catch (IOException e) {
35             e.printStackTrace();
36         }
37 
38     }
39 }

測試

 1 public class SocketTest {
 2     public static void main(String[] args) {
 3         byte[] ip = {127, 0, 0, 1};
 4         int port = 27149;
 5         Thread server = new Thread(new Server(port), "server");
 6         server.start();
 7         Thread client = new Thread(new Client(new byte[]{0,0,0,0}, 27149));
 8         client.start();
 9     }
10 }

結果

服務端針對客戶端的每條信息都能夠進行讀取並返回消息給客戶端,但是如果注釋掉寫入換行符,並判斷讀取是否是換行符的代碼,就無法讀取到流的結束。

 

Tags: