java如何實現Socket的長連接和短連接


討論Socket必討論長連接和短連接

一、長連接和短連接的概念

  1、長連接與短連接的概念:前者是整個通訊過程,客戶端和服務端只用一個Socket對象,長期保持Socket的連接;后者是每次請求,都新建一個Socket,處理完一個請求就直接關閉掉Socket。所以,其實區分長短連接就是:整個客戶和服務端的通訊過程是利用一個Socket還是多個Socket進行的。

  可能你會想:這還不簡單,長連接不就是不關Socket嘛,短連接不就是每次都關Socket每次都new Socket嘛。然而事實其實並沒有那么簡單的,請繼續看下面的整理

  2、關閉流而保持Socket正常?

    在網上百度了一下,發現很多人都是以關閉流還是關閉Socket來區分長連接和短連接的,其實,個人感覺這種區分方法並沒有什么意義:因為這里面有一個事實是,流關閉之后,便不能進行消息的發送(對應關閉輸出流)或者接受(對應關閉輸入流),因為其實關閉了對應的流,對應連接也就關閉了(這里所說的連接是發送消息的通道!),所以,流關閉而保持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
//發送核心方法
     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 =  "發送成功!" ;
         } 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;
     }

  這是我寫的一個測試的發送消息的核心方法,在關閉了對應的流(無論是輸出或者輸入)之后,下一次調用getInputStream或者getOutputStream會拋出異常說:Socket is closed;這里講明一個事實:Socket和流聯系着,流關閉了,Socket其實也就相當於關閉狀態!

  其實這個也很好理解,Socket本來就是依靠流進行關閉的,流,就只有一個,你關閉了流,Socket賴以通訊的渠道也就關閉了,與客戶的連接也斷開了,所以拋出異常是很合理的。

  所以,流關閉而要求Socket正常通訊是不可能的!

  所以,如何實現長連接?

二、長連接的正確實現方式

  1、不關閉流實現長連接?

    前面討論了,流關閉了而不關閉Socket,還是無法達到長連接的效果的,所以,要長連接,流必須不能關閉!那么,是不是直接不關閉流,然后每次要發消息就直接往流里面任進去數據,然后調用flush()方法強制刷新就行了?其實不行的,這樣客戶端是無法正常接收信息的,你會發覺就算服務端flush了,客戶端還是會一直在read方法那里阻塞!具體原因各位可以看一下java api文檔的截圖:

 

 文檔說明了,如果流一直可用,而且沒有讀到流的末尾(就是對應着對方流已經關閉或者網絡斷開!),read會一直阻塞!其實這樣做也是可以解釋清楚的:本來服務端的read方法就不知道Server端的消息什么時候發送完,說不定我以為數據發送完 了,但其實是因為網絡延遲而導致部分數據延后到來(況且也不可能所有數據同時到達),所以,read方法只能一直在阻塞等待對方的應答。所以,怎么實現長連接?

  2、實現長連接的方法

  A、客戶端自動退出開讀取的動作。前面說了,就算服務端調用了flush方法進行輸出刷新,客戶端也不一定能退出read的動作,所以還是會阻塞。所以,退出動作必須有客戶端程序自己完成,我們可以在服務端沒發送完一段消息並且刷新前就進行一個寫入結束符號的標志,客戶端解析到結束符號時,變可直接退出read的循環讀取操作,避免一直阻塞。

  B、可以調用有讀取一定字節到某個數組的read方法(不過好像這個不太行,畢竟每次消息的長度好像會變的),當然,這只是針對消息定長的情況。

  下面貼上長連接實現后的代碼(其實就是比前面的代碼加多了讀入結束標記符號)

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
//發送核心方法
     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 =  "發送成功!" ;
         } 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中還有中狀態:叫做半連接狀態,當我們只是用到輸出流的時候,我們關閉了輸出流,並且不能直接調用close方法,只能調用shutDown對應方法(具體請查看java API),其實輸入流還是連接着的(只是我們沒有用到而已!),這時候,如果沒有顯式關閉Soceket,很容易導致內存泄露,所以,所有流Socket都要顯式關閉

    2、短連接和長連接有不同的用途:對於某次服務只需要一次回話的客戶,使用短連接顯得簡單;但是,如果該次服務需要很多交互式的操作通信,那還是長連接比較高性能,畢竟,Socket的打開和關閉都是很耗性能的。

 

四、總結

  1、對應流關閉,Socket的對應輸入(出)數據的通道也就關閉,此時無法達到長連接效果;

  2、關閉Socket,記得顯式關閉流與socket,順序是線管流再關socket.

  3、要實先長連接,一般需要發送結束標記符號來告訴客戶端服務端的某段消息已經發送完畢,否則客戶端會一直阻塞在read方法。

 

原文轉載:https://www.cnblogs.com/lcplcpjava/p/6581179.html

 


免責聲明!

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



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