亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C# 實現Scoket心跳機制的方法

發布時間:2020-10-20 01:35:31 來源:腳本之家 閱讀:310 作者:PassionY 欄目:編程語言

TCP網絡長連接

手機能夠使用聯網功能是因為手機底層實現了TCP/IP協議,可以使手機終端通過無線網絡建立TCP連接。TCP協議可以對上層網絡提供接口,使上層網絡數據的傳輸建立在“無差別”的網絡之上。

建立起一個TCP連接需要經過“三次握手”:
第一次握手:客戶端發送syn包(syn=j)到服務器,并進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。

握手過程中傳送的包里不包含數據,三次握手完畢后,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。斷開連接時服務器和客戶端均可以主動發起斷開TCP連接的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是服務器和客戶端交互,最終確定斷開)

什么是心跳

剛才說到長連接建立連接后,理想狀態下是不會斷開的,但是由于網絡問題,可能導致一方斷開后,另一方仍然在發送數據,或者有些客戶端長時間不發送消息,服務器還維持這他的客戶端不必要的引用,增加了服務器的負荷。因此我們引入了心跳機制。

心跳包之所以叫心跳包是因為:它像心跳一樣每隔固定時間發一次,以此來告訴服務器,這個客戶端還活著。事實上這是為了保持長連接,至于這個包的內容,是沒有什么特別規定的,不過一般都是很小的包,或者只包含包頭的一個空包。

總的來說,心跳包主要也就是用于長連接的保活和斷線處理。一般的應用下,判定時間在30-40秒比較不錯。如果實在要求高,那就在6-9秒。

怎么發送心跳?

心跳包的發送,通常有兩種技術

方法1:應用層自己實現的心跳包 

由應用程序自己發送心跳包來檢測連接是否正常,大致的方法是:服務器在一個 Timer事件中定時 向客戶端發送一個短小精悍的數據包,然后啟動一個低級別的線程,在該線程中不斷檢測客戶端的回應, 如果在一定時間內沒有收到客戶端的回應,即認為客戶端已經掉線;同樣,如果客戶端在一定時間內沒 有收到服務器的心跳包,則認為連接不可用。

方法2:TCP的KeepAlive保活機制

因為要考慮到一個服務器通常會連接多個客戶端,因此由用戶在應用層自己實現心跳包,代碼較多 且稍顯復雜,而利用TCP/IP協議層為內置的KeepAlive功能來實現心跳功能則簡單得多。 不論是服務端還是客戶端,一方開啟KeepAlive功能后,就會自動在規定時間內向對方發送心跳包, 而另一方在收到心跳包后就會自動回復,以告訴對方我仍然在線。 因為開啟KeepAlive功能需要消耗額外的寬帶和流量,所以TCP協議層默認并不開啟KeepAlive功 能,盡管這微不足道,但在按流量計費的環境下增加了費用,另一方面,KeepAlive設置不合理時可能會 因為短暫的網絡波動而斷開健康的TCP連接。并且,默認的KeepAlive超時需要7,200,000 MilliSeconds, 即2小時,探測次數為5次。對于很多服務端應用程序來說,2小時的空閑時間太長。因此,我們需要手工開啟KeepAlive功能并設置合理的KeepAlive參數。

心跳檢測步驟:

1客戶端每隔一個時間間隔發生一個探測包給服務器
2客戶端發包時啟動一個超時定時器
3服務器端接收到檢測包,應該回應一個包
4如果客戶機收到服務器的應答包,則說明服務器正常,刪除超時定時器
5如果客戶端的超時定時器超時,依然沒有收到應答包,則說明服務器掛了

C#實現的一個簡單的心跳

using System;
using System.Collections.Generic;
using System.Threading;
 
namespace ConsoleApplication1
{
  // 客戶端離線委托
  public delegate void ClientOfflineHandler(ClientInfo client);
 
  // 客戶端上線委托
  public delegate void ClientOnlineHandler(ClientInfo client);
 
  public class Program
  {
    /// <summary>
    /// 客戶端離線提示
    /// </summary>
    /// <param name="clientInfo"></param>
    private static void ClientOffline(ClientInfo clientInfo)
    {
      Console.WriteLine(String.Format("客戶端{0}離線,離線時間:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
    }
 
    /// <summary>
    /// 客戶端上線提示
    /// </summary>
    /// <param name="clientInfo"></param>
    private static void ClientOnline(ClientInfo clientInfo)
    {
      Console.WriteLine(String.Format("客戶端{0}上線,上線時間:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
    }
 
    static void Main()
    {
      // 服務端
      Server server = new Server();
 
      // 服務端離線事件
      server.OnClientOffline += ClientOffline;
 
      // 服務器上線事件
      server.OnClientOnline += ClientOnline;
 
      // 開啟服務器
      server.Start();
 
      // 模擬100個客戶端
      Dictionary<Int32, Client> dicClient = new Dictionary<Int32, Client>();
      for (Int32 i = 0; i < 100; i++)
      {
        // 這里傳入server只是為了方便而已
        Client client = new Client(i + 1, server);
        dicClient.Add(i + 1, client);
 
        // 開啟客戶端
        client.Start();
      }
 
      System.Threading.Thread.Sleep(1000);
 
      while (true)
      {
        Console.WriteLine("請輸入要離線的ClientID,輸入0則退出程序:");
        String clientID = Console.ReadLine();
        if (!String.IsNullOrEmpty(clientID))
        {
          Int32 iClientID = 0;
          Int32.TryParse(clientID, out iClientID);
          if (iClientID > 0)
          {
            Client client;
            if (dicClient.TryGetValue(iClientID, out client))
            {
              // 客戶端離線
              client.Offline = true;
            }
          }
          else
          {
            return;
          }
        }
      }
    }
  }
 
  /// <summary>
  /// 服務端
  /// </summary>
  public class Server
  {
    public event ClientOfflineHandler OnClientOffline;
    public event ClientOnlineHandler OnClientOnline;
 
    private Dictionary<Int32, ClientInfo> _DicClient;
 
    /// <summary>
    /// 構造函數
    /// </summary>
    public Server()
    {
      _DicClient = new Dictionary<Int32, ClientInfo>(100);      
    }
 
    /// <summary>
    /// 開啟服務端
    /// </summary>
    public void Start()
    {
      // 開啟掃描離線線程
      Thread t = new Thread(new ThreadStart(ScanOffline));
      t.IsBackground = true;
      t.Start();
    }
 
    /// <summary>
    /// 掃描離線
    /// </summary>
    private void ScanOffline()
    {
      while (true)
      {
        // 一秒掃描一次
        System.Threading.Thread.Sleep(1000);
 
        lock (_DicClient)
        {
          foreach (Int32 clientID in _DicClient.Keys)
          {
            ClientInfo clientInfo = _DicClient[clientID];
 
            // 如果已經離線則不用管
            if (!clientInfo.State)
            {
              continue;
            }
 
            // 判斷最后心跳時間是否大于3秒
            TimeSpan sp = System.DateTime.Now - clientInfo.LastHeartbeatTime;
            if (sp.Seconds >= 3)
            {
              // 離線,觸發離線事件
              if (OnClientOffline != null)
              {
                OnClientOffline(clientInfo);
              }
 
              // 修改狀態
              clientInfo.State = false;
            }
          }
        }
      }
    }
 
    /// <summary>
    /// 接收心跳包
    /// </summary>
    /// <param name="clientID">客戶端ID</param>
    public void ReceiveHeartbeat(Int32 clientID)
    {
      lock (_DicClient)
      {
        ClientInfo clientInfo;
        if (_DicClient.TryGetValue(clientID, out clientInfo))
        {
          // 如果客戶端已經上線,則更新最后心跳時間
          clientInfo.LastHeartbeatTime = System.DateTime.Now;
        }
        else
        {
          // 客戶端不存在,則認為是新上線的
          clientInfo = new ClientInfo();
          clientInfo.ClientID = clientID;
          clientInfo.LastHeartbeatTime = System.DateTime.Now;
          clientInfo.State = true;
 
          _DicClient.Add(clientID, clientInfo);
 
          // 觸發上線事件
          if (OnClientOnline != null)
          {
            OnClientOnline(clientInfo);
          }
        }
      }
    }
  }
 
  /// <summary>
  /// 客戶端
  /// </summary>
  public class Client
  {
    public Server Server;
    public Int32 ClientID;
    public Boolean Offline;
 
    /// <summary>
    /// 構造函數
    /// </summary>
    /// <param name="clientID"></param>
    /// <param name="server"></param>
    public Client(Int32 clientID, Server server)
    {
      ClientID = clientID;
      Server = server;
      Offline = false;
    }
 
    /// <summary>
    /// 開啟客戶端
    /// </summary>
    public void Start()
    {
      // 開啟心跳線程
      Thread t = new Thread(new ThreadStart(Heartbeat));
      t.IsBackground = true;
      t.Start();
    }
 
    /// <summary>
    /// 向服務器發送心跳包
    /// </summary>
    private void Heartbeat()
    {
      while (!Offline)
      {
        // 向服務端發送心跳包
        Server.ReceiveHeartbeat(ClientID);
         
        System.Threading.Thread.Sleep(1000);
      }
    }
  }
 
  /// <summary>
  /// 客戶端信息
  /// </summary>
  public class ClientInfo
  {
    // 客戶端ID
    public Int32 ClientID;
 
    // 最后心跳時間
    public DateTime LastHeartbeatTime;
 
    // 狀態
    public Boolean State;
  }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

光山县| 会泽县| 贵定县| 宕昌县| 运城市| 白朗县| 阿勒泰市| 肃宁县| 岐山县| 安岳县| 马山县| 黑龙江省| 密云县| 应城市| 荃湾区| 南郑县| 交城县| 喜德县| 南投市| 杭州市| 长沙市| 余庆县| 海淀区| 台南县| 台中市| 彩票| 张家口市| 苍南县| 康平县| 宽甸| 靖西县| 恩施市| 光山县| 通州市| 环江| 昭平县| 象山县| 皋兰县| 绵阳市| 东海县| 壤塘县|