java如何实现Socket的长连接和短连接
討論Socket必討論長連接和短連接
一、長連接和短連接的概念
1、長連接與短連接的概念:前者是整個通訊過程,客戶端和服務端只用一個Socket對象,長期保持Socket的連接;后者是每次請求,都新建一個Socket,處理完一個請求就直接關閉掉Socket。所以,其實區(qū)分長短連接就是:整個客戶和服務端的通訊過程是利用一個Socket還是多個Socket進行的。
可能你會想:這還不簡單,長連接不就是不關Socket嘛,短連接不就是每次都關Socket每次都new Socket嘛。然而事實其實并沒有那么簡單的,請繼續(xù)看下面的整理
2、關閉流而保持Socket正常?
在網上百度了一下,發(fā)現(xiàn)很多人都是以關閉流還是關閉Socket來區(qū)分長連接和短連接的,其實,個人感覺這種區(qū)分方法并沒有什么意義:因為這里面有一個事實是,流關閉之后,便不能進行消息的發(fā)送(對應關閉輸出流)或者接受(對應關閉輸入流),因為其實關閉了對應的流,對應連接也就關閉了(這里所說的連接是發(fā)送消息的通道!),所以,流關閉而保持Socket開啟,是沒有達到長連接的效果,貼上測試代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //發(fā)送核心方法 ????public?String send(String send)?throws?IOException { ????????String rtn =?null; ????????BufferedWriter writer =?null; ????????OutputStreamWriter ow =?null; ????????OutputStream os =?null; ????????try{ ????????????os = socket.getOutputStream(); ????????????ow =?new?OutputStreamWriter(os); ????????????writer =?new?BufferedWriter(ow); ????????????char?[] sendChar = send.toCharArray(); ????????????ArrayList<Integer> list =?new?ArrayList<Integer>(); ????????????for(char?ch:sendChar){ ????????????????list.add((int)ch); ????????????} ????????????//進行加密操作 ????????????list = encry(list); ????????????Iterator<Integer> it = list.iterator(); ????????????while(it.hasNext()){ ????????????????writer.write(it.next()); ????????????} ????????????writer.flush(); ????????????rtn =?"發(fā)送成功!"; ????????}finally{ ????????????//注意:直接關閉流將會導致socket關閉,只能通過shutdownOutput/input的方式關閉流 ????????????//另外,流關閉之后,相當于關閉底層的連接,除非新new個socket,否則和客戶端的連接相當于斷開 //????????? if(writer!=null){ //????????????? writer.close(); //????????? } //????????? if(ow!=null){ //????????????? ow.close(); //????????? } //????????? if(os!=null){ ????????????????//os.close(); //????????? } ????????????//socket.shutdownOutput();流關閉之后,相當于關閉底層的連接,除非新<br>new個socket,否則和客戶端的連接相當于斷開 ????????} ????????return?rtn; ????} |
這是我寫的一個測試的發(fā)送消息的核心方法,在關閉了對應的流(無論是輸出或者輸入)之后,下一次調用getInputStream或者getOutputStream會拋出異常說:Socket is closed;這里講明一個事實:Socket和流聯(lián)系著,流關閉了,Socket其實也就相當于關閉狀態(tài)!
其實這個也很好理解,Socket本來就是依靠流進行關閉的,流,就只有一個,你關閉了流,Socket賴以通訊的渠道也就關閉了,與客戶的連接也斷開了,所以拋出異常是很合理的。
所以,流關閉而要求Socket正常通訊是不可能的!
所以,如何實現(xiàn)長連接?
二、長連接的正確實現(xiàn)方式
1、不關閉流實現(xiàn)長連接?
前面討論了,流關閉了而不關閉Socket,還是無法達到長連接的效果的,所以,要長連接,流必須不能關閉!那么,是不是直接不關閉流,然后每次要發(fā)消息就直接往流里面任進去數(shù)據,然后調用flush()方法強制刷新就行了?其實不行的,這樣客戶端是無法正常接收信息的,你會發(fā)覺就算服務端flush了,客戶端還是會一直在read方法那里阻塞!具體原因各位可以看一下java api文檔的截圖:
?
?文檔說明了,如果流一直可用,而且沒有讀到流的末尾(就是對應著對方流已經關閉或者網絡斷開!),read會一直阻塞!其實這樣做也是可以解釋清楚的:本來服務端的read方法就不知道Server端的消息什么時候發(fā)送完,說不定我以為數(shù)據發(fā)送完 了,但其實是因為網絡延遲而導致部分數(shù)據延后到來(況且也不可能所有數(shù)據同時到達),所以,read方法只能一直在阻塞等待對方的應答。所以,怎么實現(xiàn)長連接?
2、實現(xiàn)長連接的方法
A、客戶端自動退出開讀取的動作。前面說了,就算服務端調用了flush方法進行輸出刷新,客戶端也不一定能退出read的動作,所以還是會阻塞。所以,退出動作必須有客戶端程序自己完成,我們可以在服務端沒發(fā)送完一段消息并且刷新前就進行一個寫入結束符號的標志,客戶端解析到結束符號時,變可直接退出read的循環(huán)讀取操作,避免一直阻塞。
B、可以調用有讀取一定字節(jié)到某個數(shù)組的read方法(不過好像這個不太行,畢竟每次消息的長度好像會變的),當然,這只是針對消息定長的情況。
下面貼上長連接實現(xiàn)后的代碼(其實就是比前面的代碼加多了讀入結束標記符號)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | //發(fā)送核心方法 ????public?String send(String send)?throws?IOException { ????????String rtn =?null; ????????BufferedWriter writer =?null; ????????OutputStreamWriter ow =?null; ????????OutputStream os =?null; ????????try{ ????????????os = socket.getOutputStream(); ????????????ow =?new?OutputStreamWriter(os); ????????????writer =?new?BufferedWriter(ow); ????????????char?[] sendChar = send.toCharArray(); ????????????ArrayList<Integer> list =?new?ArrayList<Integer>(); ????????????for(char?ch:sendChar){ ????????????????list.add((int)ch); ????????????} ????????????//進行加密操作 ????????????list = encry(list); ????????????Iterator<Integer> it = list.iterator(); ????????????while(it.hasNext()){ ????????????????writer.write(it.next()); ????????????} ????????????//寫入結束標志符號:% ????????????writer.write('%'); ????????????writer.flush(); ????????????rtn =?"發(fā)送成功!"; ????????}finally{ ????????????//注意:直接關閉流將會導致socket關閉,只能通過shutdownOutput/input的方式關閉流 ????????????//另外,流關閉之后,相當于關閉底層的連接,除非新new個socket,否則和客戶端的連接相當于斷開 //????????? if(writer!=null){ //????????????? writer.close(); //????????? } //????????? if(ow!=null){ //????????????? ow.close(); //????????? } //????????? if(os!=null){ ????????????????//os.close(); //????????? } ????????????//socket.shutdownOutput();流關閉之后,相當于關閉底層的連接,除非新new個socket,否則和客戶端的連接相當于斷開 ????????} ????????return?rtn; ????} |
三、短連接
短連接就基本沒什么好講的啦,只是每次關閉Socket和流時需要注意一下事情:
1、雖然前面說了流關閉了,Socket就不可用了,但是,我們還是要顯式的關閉Socket的,因為在Socekt中還有中狀態(tài):叫做半連接狀態(tài),當我們只是用到輸出流的時候,我們關閉了輸出流,并且不能直接調用close方法,只能調用shutDown對應方法(具體請查看java API),其實輸入流還是連接著的(只是我們沒有用到而已!),這時候,如果沒有顯式關閉Soceket,很容易導致內存泄露,所以,所有流Socket都要顯式關閉
2、短連接和長連接有不同的用途:對于某次服務只需要一次回話的客戶,使用短連接顯得簡單;但是,如果該次服務需要很多交互式的操作通信,那還是長連接比較高性能,畢竟,Socket的打開和關閉都是很耗性能的。
?
四、總結
1、對應流關閉,Socket的對應輸入(出)數(shù)據的通道也就關閉,此時無法達到長連接效果;
2、關閉Socket,記得顯式關閉流與socket,順序是線管流再關socket.
3、要實先長連接,一般需要發(fā)送結束標記符號來告訴客戶端服務端的某段消息已經發(fā)送完畢,否則客戶端會一直阻塞在read方法。
?
原文轉載:https://www.cnblogs.com/lcplcpjava/p/6581179.html
?
轉載于:https://www.cnblogs.com/AndyAo/p/8232700.html
總結
以上是生活随笔為你收集整理的java如何实现Socket的长连接和短连接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CMOS中的 latch-up 闩锁效应
- 下一篇: zsh: command not fou