【Socket】關於socket長連線的心跳包

Leo.cheng發表於2014-02-24

TCP的socket本身就是長連線的,那麼為什麼還要心跳包呢? 

  在smack裡有個30s傳送一個空訊息的執行緒,同樣關於心跳包(keepalive)

  據網路搜尋到的資料解釋如下

  1. 內網機器如果不主動向外發起連線,外網機沒法直連內網的,這也是內網機安全的原因之一,又因為路由器會把這個關係記錄下來,但是過一段時間這個記錄可能會丟失 ,所有每一個客戶端每隔一定時間就會向伺服器傳送訊息,以保證伺服器可以隨時找到你,這東西被稱為心跳包。 
  2. 理論上說,這個連線是一直保持連線的,但是實際情況中,如果中間節點出現什麼故障是難以知道的。更要命的是,有的節點(防火牆)會自動把一定時間之內沒有資料互動的連線給斷掉。在這個時候,就需要我們的心跳包了,用於維持長連線,保活。在獲知了斷線之後,伺服器邏輯可能需要做一些事情,比如斷線後的資料清理,重新連線……當然,這個自然是要由邏輯層根據需求去做了。總的來說,心跳包主要也就是用於長連線的保活和斷線處理。一般的應用下,判定時間在30-40秒比較不錯。如果實在要求高,那就在6-9秒。 
  3. 如果不主動關閉socket的話,系統不會自動關閉的,除非當前程式掛掉了,作業系統把佔用的socket回收了才會關閉。為什麼需要心跳連線主要是判斷當前連線是否是有效的、可被使用的。在實際應用中假設一段時間沒有資料傳輸時候理論上說應該連線是沒有問題的,但是網路複雜,中途出現問題也是常見的,網線被掐斷了、對方程式掛掉了、頻繁丟包等,這時候TCP連線是不可使用的,但是對於應用層並不知道,如果需知道網路情況則要很複雜的超時進行了解,TCP從底層就實現了這樣的功能。心跳機制是TCP在一段時間間隔後傳送確認連線端是否還存在,如果存在的話就會回傳一個包確定網路有效,如果心跳包有問題,則通知上層應用當前網路有問題了。 
  4. 這取決於你的server端的超時配置, 每個socket連線都是長連線,它是一個相當佔用系統資源的通訊管道, 如果這個長連線什麼事也沒幹硬是要佔著資源,則server端可以選擇關閉這個連線,以省下資源讓更多的使用者連線進來。 
  5. 所以,即便客戶端的是採用死迴圈while(true)方式連到服務端,對於特定的客戶端和服務端型別來說也需要一定時間間隔的心跳(告訴服務端,我還活著,雖然我沒幹活也沒說話,但別把我關了) 

  以前開發手機遊戲時,索愛有一款手機有強制要求,客戶端如果超過三分鐘無訊息發向網路服務端,則會在客戶端自動地強制把socket關斷。因為socket長連線相對於手機這樣資源少的裝置來說是寶貴的資源。  (這個強制是指客戶端系統自動關的,不是我們程式碼close的) 

  1.  在TCP的機制裡面,本身是存在有心跳包的機制的,也就是TCP的選項:SO_KEEPALIVE。系統預設是設定的2小時的心跳頻率。但是它檢查不到機器斷電、網線拔出、防火牆這些斷線。而且邏輯層處理斷線可能也不是那麼好處理。一般,如果只是用於保活還是可以的。
  2. 心跳包一般來說都是在邏輯層傳送空的echo包來實現的。下一個定時器,在一定時間間隔下傳送一個空包給客戶端,然後客戶端反饋一個同樣的空包回來,伺服器如果在一定時間內收不到客戶端傳送過來的反饋包,那就只有認定說掉線了。
  3. 其實,要判定掉線,只需要send或者recv一下,如果結果為零,則為掉線。

  以下是Smack裡傳送心跳包的程式碼:PacketWrite.java

void startKeepAliveProcess()  
{  
    int keepAliveInterval = SmackConfiguration.getKeepAliveInterval();  
    if (keepAliveInterval > 0) 
    {  
      KeepAliveTask task = new KeepAliveTask(keepAliveInterval);  
      this.keepAliveThread = new Thread(task);  
      task.setThread(this.keepAliveThread);  
      this.keepAliveThread.setDaemon(true);  
      this.keepAliveThread.setName("Smack Keep Alive (" + this.connection.connectionCounterValue + ")");  
      this.keepAliveThread.start();  
    }  
}  
  
private class KeepAliveTask implements Runnable  
{  
    private int delay;  
    private Thread thread;  
  
    public KeepAliveTask(int paramInt)  
    {  
      this.delay = paramInt;  
    }  
  
    protected void setThread(Thread thread) 
    {  
      this.thread = thread;  
    }  
  
    public void run()  
    {  
      try  
      {  
        Thread.sleep(15000L);  
      }  
      catch (InterruptedException localInterruptedException)  
      {  
      }  
      while ((!(PacketWriter.this.done)) && (PacketWriter.this.keepAliveThread == this.thread)) 
      {  
            synchronized (PacketWriter.this.writer)  
            {  
                  if (System.currentTimeMillis() - PacketWriter.this.lastActive >= this.delay) 
                  {  
                        try 
                        {  
                          PacketWriter.this.writer.write(" ");  
                          PacketWriter.this.writer.flush();  
                        }  
                        catch (Exception localException)  
                        {  
                        }  
                  }  
            }  
            try  
            {  
              Thread.sleep(this.delay);  
            }  
            catch (InterruptedException localInterruptedException1)  
            {  
            }  
      }  
    }  
}  
View Code

   另外記一個CSDN上早些年的問題:

  1. QQ等程式聊天,雙方是否使用SOCKET通訊?應該不會都是通過伺服器吧,那樣伺服器負擔得多大啊?
  2. 如果是SOCKET,那麼多的好友,一下子得建立多少個啊?還得考慮上線下線隱身等亂七八糟的問題。
  3. 請高手幫忙指點一下這種複雜程式的結構,謝謝。

  我還是很有發言權的呀,我畢業設計就是寫的這個(不過當時用的是vc寫的,帶檔案傳輸的)@hl_ghost
  我來說下我的思路吧:
1.如何知道誰線上?
      Server維護一個list就ok了(存所有人的ip,名字,線上等)
2.如何讓伺服器隨時能找到你?
   前提:內網機器如果不主動向外發起連線,外網機沒法直連內網的,這也是內網機安全的原因之一吧,又因為路由器會把這個關係記錄下來,但是過一段時間這個記錄可能會丟失 ,所有每一個客戶端    每隔一定時間就會向伺服器傳送訊息,以保證伺服器可以隨時找到你,這東西被稱為心跳包。
3.如何跨內網直連
   Nat打洞(難):
   我簡單說下原理,有兩個客戶端A,B ,當然必須有Server啦(他可以隨時連線A,B)
   當A想連B時,A就回從Server那要B的ip,然後與B建立連線(第一次不能成功的,因為看紅字)。
   這時A告訴Server,我找不到B,你替我告訴他一聲,我想與它連線,伺服器就告訴B,你給A下一個請帖(B發請求向A)! 
   這時A再向B發起連線就可以成功了(以後就不用server幫忙了)。
4.如何保證資料的可靠性(難)
   滑動視窗協議,這個一句話兩句說不清楚啦,自己google下。
5是否線上。
     我的設計是每隔40秒客戶端把Server中存自己的資訊中的線上改為真,而伺服器每過45秒就檢查這個線上變數是否為真,真的話把他改成假,如果假的話就說明這個人在45秒沒有向Server報到=>他網路出現異常了,掉線了,向其它人發這個人的掉線通知。(這麼設計原因在於當使用者網斷了沒有發下線通知,我們也能知道他不線上了)
6檔案傳輸(難)
   把檔案讀到buf裡,然後每次發1024b(當收到接收方確認後再發下一個1024b)。

 

參考文章

  http://www.cppblog.com/tx7do/archive/2009/11/09/100513.html

  http://bbs.csdn.net/topics/270063434

 

相關文章