關於java socket中的read方法阻塞問題


    前幾天一個有個同學咨詢我關於java socket編程的一些問題,因為我這個同學今年剛從.NET轉到java 對於java的IO體系不是很清楚,在給他解答一些問題時我自己也總結了比較容易出錯的問題。

  我們直接貼一段socket代碼看一下

客戶端:
public class SocketClient {
	
	public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
		Socket client = new Socket("localhost",8888);
		OutputStream out = client.getOutputStream();
		InputStream input = client.getInputStream();

		out.write("sender say hello socket".getBytes());
		out.flush();
		
		read(input);
		out.close();
	}
	public static void read(InputStream input) throws IOException {
		 byte[] buf = new byte[128];
         int size = 0;
         while ((size = input.read(buf,0,buf.length)) != -1) {
        	 System.out.print(new String(buf));
         }
	}
}


服務端:
public class SocketServer {
	public static void main(String[] args) {
        SocketServer ss = new SocketServer();
        int port = 8888;
        try {
            ss.startServer(port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void startServer(int port) throws Exception {
        ServerSocket serverSocket = new ServerSocket(port);
        Socket server = null;
        try {
            while (true) {
                server = serverSocket.accept();
                System.out.println("server socket is start……");
                try {
                    BufferedReader input = new BufferedReader(new InputStreamReader(new ByteArrayInputStream("服務端發給客戶端的信息".getBytes())));
                    BufferedInputStream in = new BufferedInputStream(server.getInputStream());
                    PrintWriter out = newPrintWriter(newOutputStreamWriter(server.getOutputStream()));
                    
                    String clientstring = null;
                    out.println("歡迎客戶端連入");
                    out.flush();
                    byte[] buf = new byte[128];
                    int size = 0;
                    while (( size = in.read(buf,0,buf.length)) != -1) {
                    	System.out.println(new String(buf));
                    }
                    out.print(" client is over");
                    out.flush();
                    out.close();
                    System.out.println(" client is over");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    server.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            serverSocket.close();
        }
    }
    
   
}

  

   這段代碼執行以后會發現server類 read()方法發生了阻塞,經過查找資料發現 read() 是一個阻塞函數,如果客戶端沒有聲明斷開outputStream那么它就會認為客戶端仍舊可能發送數據,像read()這種阻塞讀取函數還有 BufferedReader 類種的 readLine()、DataInputStream種的readUTF()等。

  下面我們來看下一些解決方案

   1)Socket任意一端在調用完write()方法時調用shutdownOutput()方法關閉輸出流,這樣對端的inputStream上的read操作就會返回-1, 這里我們要注意下不能調用socket.getInputStream().close()。因為它會導致socket直接被關閉。 當然如果不需要繼續在socket上進行讀操作,也可以直接關閉socket。但是這個方法不能用於通信雙方需要多次交互的情況。

    out.write("sender say hello socket".getBytes());
    out.flush();
    client.shutdownOutput();  //調用shutdown 通知對端請求完畢

  

 
 這個解決方案缺點非常明顯,socket任意一端都依賴於對方調用 shutdownOutput()來完成read返回 -1,如果任意一方沒有執行shutdown函數那么就會出現問題。所以一般我們都會在socket請求時設置連接的超時時間  socket.setSoTimeout(5000);以防止長時間沒有響應造成系統癱瘓。
   while (true) {
        server = serverSocket.accept();
        System.out.println("server socket is start……");
        server.setSoTimeout(5000);
        .....
    }

  

2)發送數據時,約定數據的首部固定字節數為數據長度。這樣讀取到這個長度的數據后,就不繼續調用read方法
 這種方式優點是不依賴對方調用shutdown函數,響應較快,缺點是數據傳輸是最大字節數固定,需雙方事先約定好長度,伸縮性差。
 
3)發送數據時,約定前幾位返回數據byte[]長度大小或最后輸出 \n 或 \r 作為數據傳輸終止符。
客戶端

        out.write("sender say hello socket \n".getBytes());
	out.flush();

服務器端

        byte[] buf = new byte[1];
        int size = 0;
        StringBuffer sb = new StringBuffer();
        while (( size = in.read(buf,0,buf.length)) != -1) {
            String str = new String(buf);
            if(str.equals("\n")) {
                break;
            }
            sb.append(str); 
            System.out.print(str);
        }

 這種方式是對第二種方案的改良版,但不得不說 這是目前socket數據傳輸的最常用處理read()阻塞的解決方案。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM