Socket程式設計 之 一種死鎖現象
剛接觸socket程式設計的過程中,很容易出現死鎖的現象。下面我來介紹一種死鎖的原因和解決的方法。
先來看這段程式碼:
/*客戶端傳送一個資訊到服務端*/
Socket socket = new Socket("127.0.0.1", 8081);
outputStream = socket.getOutputStream();
outputStream.write("伺服器我想要一個html檔案".getBytes());
/*接受伺服器傳來的響應資訊*/
inputStreams = socket.getInputStream();
byte[] bytes = new byte [1024];
StringBuilder stringBuilder = new StringBuilder();
int len = 0;
while((len = inputStreams.read(bytes))!=-1){
stringBuilder.append(new String(bytes,0,len));
}
程式碼很簡單。就是先往伺服器傳送一個資訊,然後伺服器解析完資訊後,傳回一個響應的內容。然後客戶端再接受該響應。
再來看伺服器端的程式碼:
ServerSocket serverSocket = new ServerSocket(8081, 1, InetAddress.getByName("127.0.0.1" ));
/*接受客戶端的socket,若沒有連線,會一直阻塞到有連線進來*/
Socket socket = serverSocket.accept();
/*伺服器解析客戶端傳來的資訊*/
inputStream = socket.getInputStream();
StringBuilder sb = new StringBuilder();
byte[] bytes = new byte[1024];
int len = 0;
while((len=inputStream.read(bytes))!=-1){
sb.append(new String(bytes,0,len));
}
/*伺服器解析完傳來的資訊後,做出響應*/
outputStream = socket.getOutputStream();
outputStream.write("html檔案來了".getBytes());
1.假設伺服器的執行緒先與客戶端執行。ServerSocket被執行到,然後呼叫accept();處於阻塞,一直等到一個socket進來。
2.與此同時客戶端生成了一個socket並且傳來了一個資訊“伺服器我想要一個html檔案”。writer被執行後會繼續執行到inputStreams.read(),因為read是阻塞的會一直等到服務端把響應資訊傳回。所以出於阻塞中。
3.accept()接受到socket之後就會去解析客戶端傳來的資訊了,執行到inputStream.read()的時候,也會被阻塞。有人問客戶端不是傳來了“伺服器我想要一個html檔案”的資訊了嗎?為什麼read還會被阻塞啊。這是因為read還沒有讀到-1。
伺服器沒有讀到-1,會以為還有資訊要傳來,於是也處於等到中。可是客戶端已經傳輸好了,並且在等伺服器給響應資訊呢。於是就發生了死鎖。
解決的辦法有兩種:
第一種方法close流,相當於給流中加入一個結束標記-1
第二種方法:s.shutdownOutput();//關閉客戶端的輸出流。
相當於給流中加入一個結束標記-1.這個樣子伺服器的輸入流的reaLine方法就會讀到一個-1,然後結束readLIne方法。也可以這樣理解s.shutdownOutput();關閉的是客戶端的輸出流,同時伺服器端的輸入流也隨之關閉。s.shutdownIntput();也是同樣的道理,關閉客戶端的輸入流,同時伺服器端的輸出流也隨之關閉。
通過socket.shutdownOutput()關閉輸出流,但socket仍然是連線狀態,連線並未關閉
如果直接關閉輸入或者輸出流,即:in.close()或者out.close(),會直接關閉socket