【Java】Java socket通訊使用read,readline函式的阻塞問題
阿新 • • 發佈:2020-11-30
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 }
結果
服務端針對客戶端的每條資訊都能夠進行讀取並返回訊息給客戶端,但是如果註釋掉寫入換行符,並判斷讀取是否是換行符的程式碼,就無法讀取到流的結束。