淺談java socket的正確關(guān)閉姿勢
java socket對應(yīng)的是網(wǎng)絡(luò)協(xié)議中的tcp,tcp的三次握手、四次揮手、11中狀態(tài)什么的這里就不說了,不知道大家平常使用socket的時候如果不注意的情況下,會不會遇到各種異常報錯。
例如:
java.net.SocketException:socket is closed
錯誤提示的出現(xiàn)場景:
自己主動關(guān)閉了socket,但是之后還從里面讀寫數(shù)據(jù)
Software caused connection abort: socket write error
錯誤提示的出現(xiàn)場景:
對方已經(jīng)關(guān)閉socket,依舊向?qū)Ψ綄憯?shù)據(jù)
connection reset (by peer)
錯誤提示出現(xiàn)的場景:
一端socket被關(guān)閉,另一端仍然發(fā)送數(shù)據(jù),發(fā)送的第一個數(shù)據(jù)包 connection reset by peer
一端socket退出,退出時為關(guān)閉連接,另一端讀數(shù)據(jù) connection reset
所以在使用socket時,需要約定好雙方讀寫完成的條件,然后關(guān)閉輸入輸出流:
socket.shutdownInput();socket.shutdownOutput();
即當(dāng)一方寫入完成后,調(diào)用shutdownOutput關(guān)閉輸出流,這時候?qū)Ψ降膔ead方法就會返回-1,這時候?qū)Ψ骄椭滥銓懲炅耍瑢Ψ娇梢躁P(guān)閉輸入流,然后等待對方寫入完成調(diào)用shutdownOutput后己方再調(diào)用shutdownInput,雙方就正常關(guān)閉了輸入輸出流,這時候socket就不會出現(xiàn)異常了。
下面是一個socket交互的例子:server端
public class OioServer { public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);while (true) { Socket socket = serverSocket.accept(); System.out.println('socket = ' + socket); new Thread(() -> {try { InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write('hello! I get your message that is follow'.getBytes(Charset.forName('UTF-8'))); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) != -1) {System.out.print(new String(buf, 0, len, Charset.forName('UTF-8')));out.write(buf, 0, len); } out.write('n end n'.getBytes(Charset.forName('UTF-8'))); out.flush(); socket.shutdownInput(); socket.shutdownOutput();} catch (IOException e) { e.printStackTrace();}finally { try {socket.close(); } catch (IOException e) {e.printStackTrace(); }} }).start();} }}
client端
public class OioClient { public static void main(String[] args) throws IOException {Socket socket = new Socket('127.0.0.1', 8080);InputStream in = socket.getInputStream();new Thread(() -> { BufferedInputStream bufferIn = new BufferedInputStream(in); byte[] buf = new byte[1024]; try {int len;while ((len = bufferIn.read(buf)) != -1) { System.out.print(new String(buf, 0, len, Charset.forName('UTF-8')));} }catch (Exception e) {e.printStackTrace(); } try {socket.shutdownInput();socket.close(); } catch (IOException e) {e.printStackTrace(); }}).start();OutputStream out = socket.getOutputStream();int cout = 10;while (cout-- > 0) { out.write(('this time is ' + System.currentTimeMillis() + 'n').getBytes('UTF-8'));}socket.shutdownOutput(); }}java socket - 半關(guān)閉
通常,使用關(guān)閉輸出流來表示輸出已經(jīng)結(jié)束。但在進行網(wǎng)絡(luò)通信時則不能這樣做。因為我們關(guān)閉輸出流時,該輸出流對應(yīng)的Socket也將隨之關(guān)閉,這樣程序?qū)o法再從該socket中讀取數(shù)據(jù)。
為了應(yīng)付這種情況,socket提供了兩個半關(guān)閉的方法用來只關(guān)閉socket的輸入流或者輸出流,用以表示輸出數(shù)據(jù)已經(jīng)發(fā)送完成。
方法詳情:
shutdownInput():關(guān)閉該socket的輸入流,程序還可以通過該socket的輸出流輸出數(shù)據(jù);
shutdownOutput():關(guān)閉該socket的輸出流,程序還可以通過該socket的輸入流讀取數(shù)據(jù)。
當(dāng)調(diào)用shutdownInput()或shutdownOutput()方法關(guān)閉輸入流或輸出流后,該socket處于半關(guān)閉狀態(tài)。
此時可以使用isInputShutdown()或isOutputShutdown()來判斷該socket是否處于半讀狀態(tài)或半寫狀態(tài)。
需要注意的是,即使同一個socket先后調(diào)用shutdownInput()和shutdownInput()方法,該socket實例仍然沒有被關(guān)閉,只是該socket既不能輸出數(shù)據(jù)也不能讀取數(shù)據(jù)而已。
當(dāng)調(diào)用shutdownInput()或shutdownOutput()方法關(guān)閉了輸入流或輸出流之后,該socket無法再次打開輸出流或輸入流,因此這種做法不適合需要保持持久通信狀態(tài)的交互式應(yīng)用。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. Echarts通過dataset數(shù)據(jù)集實現(xiàn)創(chuàng)建單軸散點圖2. Laravel操作session和cookie的教程詳解3. css進階學(xué)習(xí) 選擇符4. 阿里前端開發(fā)中的規(guī)范要求5. 解析原生JS getComputedStyle6. XML入門精解之結(jié)構(gòu)與語法7. XML入門的常見問題(一)8. 將properties文件的配置設(shè)置為整個Web應(yīng)用的全局變量實現(xiàn)方法9. html小技巧之td,div標(biāo)簽里內(nèi)容不換行10. PHP字符串前后字符或空格刪除方法介紹
