歡迎您的來訪!有源碼,好建站(m.bmm520.net)源碼海洋源碼網(wǎng)為您提供快速建站平臺.
      當(dāng)前位置: 首頁 > 行業(yè)資訊 > c# .net 資料 >

      C#實現(xiàn)TCP客戶端,可檢測斷線并自動重連保持連接,對于網(wǎng)線斷開或拔掉的情況也可檢測到

      時間:2021-07-11 23:39來源:未知 作者:admin 點擊:
      使用C#實現(xiàn)的TCP客戶端,可檢測斷線(包括網(wǎng)線斷開或拔掉),支持?jǐn)嗑重連。客戶端內(nèi)部有循環(huán)緩沖區(qū)異步接收數(shù)據(jù),對客戶端的讀操作會立即返回,根據(jù)方法返回值來判斷讀取數(shù)據(jù)的字節(jié)數(shù)。 對外提供3個事件分別是: 收到數(shù)據(jù)事件(網(wǎng)絡(luò)收到數(shù)據(jù)觸發(fā)此事件) 通訊空

       使用C#實現(xiàn)的TCP客戶端,可檢測斷線(包括網(wǎng)線斷開或拔掉),支持?jǐn)嗑重連。客戶端內(nèi)部有循環(huán)緩沖區(qū)異步接收數(shù)據(jù),對客戶端的讀操作會立即返回,根據(jù)方法返回值來判斷讀取數(shù)據(jù)的字節(jié)數(shù)。

       
      對外提供3個事件分別是:
       
          收到數(shù)據(jù)事件(網(wǎng)絡(luò)收到數(shù)據(jù)觸發(fā)此事件)
          通訊空閑事件(長時間無數(shù)據(jù)收發(fā),時間可設(shè))
          重連失敗事件(默認(rèn)最大支持3次重連,若3次重連仍然失敗則觸發(fā)事件)
       
      使用方式:
      private Thread _socketThread;
      private TcpClient _client;
       
      _socketThread = new Thread(_client.ReadFormSocket);
      _socketThread.IsBackground = false;
      _socketThread.Start();
       
      //接收數(shù)據(jù)操作,除此之外還可以注冊接收事件,有數(shù)據(jù)來時讀取
      byte [] recv = new byte[100];
      _client.CommReceive(recv,100);
       
      //發(fā)送數(shù)據(jù)
      byte [] send = new byte[10];
      for (int i = 0; i < 10; i++)
          send[i] = i;
      _client.CommSend(send,10);
      ————————————————
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Net.Sockets;
      using System.Net;
      using System.Timers;
       
      namespace Communication
      {
          //通訊接口事件代理
          internal delegate void CommRxEventHandler();
          internal delegate void CommIdleEventHandler();
       
          //通訊接口
          internal interface ICommunication
          {
              event CommRxEventHandler CommRxEvent;       //收到數(shù)據(jù)事件
              event CommIdleEventHandler CommIdleEvent;   //通訊空閑事件
       
              int CommSend(byte[] buffer, int size);      //發(fā)送數(shù)據(jù)到通訊口
              int CommReceive(CommBuffer buffer);         //從通訊口接收數(shù)據(jù)
              int CommReceive(byte[] buffer, int size);   //從通訊口接收數(shù)據(jù)
          }
       
          //TCP客戶端實現(xiàn)
          internal class TcpClient : ICommunication
          {
              private const int MAX_RECONNECT_TIMES = 3;  //斷線重連嘗試次數(shù)
              private const int COMM_IDLE_TIMES = 3;      //通訊空閑觸發(fā)間隔
       
              //soket對象及參數(shù)
              private Socket _socket;     
              private string _host;       
              private int _port;
              private bool _reconnect;
              private int ConnecteFailedCount { get; set; }
              public int ReconnectStatistics { get; private set; } 
       
              private static uint _keepAliveTime = 5000;      //無數(shù)據(jù)交互持續(xù)時間(ms)
              private static uint _keepAliveInterval = 500;   //發(fā)送探測包間隔(ms)
       
              //定時器,用于觸發(fā)通訊空閑
              private Timer _timer;
              private int _commIdleCount;
       
              //實現(xiàn)接口的兩個事件
              public event CommRxEventHandler CommRxEvent;
              public event CommIdleEventHandler CommIdleEvent;
       
              //重連失敗事件
              public event EventHandler ReconnectionFailedEvent;
       
              //數(shù)據(jù)接收緩存
              private CommBuffer recvBuffer;
       
              //構(gòu)造函數(shù)
              public TcpClient(string host,int port)
              {
                  _host = host;
                  _port = port;
                  _reconnect = false;
                  ConnecteFailedCount = 0;
                  recvBuffer = new CommBuffer(20480);
              }
              
              //連接
              public void Connect()
              {
                  _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                      ProtocolType.Tcp);
                  _socket.Connect(_host, _port);
                  ConnecteFailedCount = MAX_RECONNECT_TIMES;
       
                  //設(shè)置KeepAlive
                  _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                  byte[] optionValue = new byte[12];
                  BitConverter.GetBytes(1).CopyTo(optionValue, 0);
                  BitConverter.GetBytes(_keepAliveTime).CopyTo(optionValue, 4);
                  BitConverter.GetBytes(_keepAliveInterval).CopyTo(optionValue, 8);
                  _socket.IOControl(IOControlCode.KeepAliveValues, optionValue, null);
              }
       
              //重連
              public bool Reconnect()
              {
                  ReconnectStatistics++;
                  _reconnect = false;
                  Close();
                  try
                  {
                      Connect();
                  }
                  catch (SocketException e)
                  {
                      ConnecteFailedCount--;
                      if (ConnecteFailedCount > 0)
                      {
                          //Console.WriteLine("重試次數(shù)剩余{0}",ConnecteFailedCount);
                          _reconnect = true;
                          return true;
                      }
                      else
                      {
                          //重連失敗事件
                          if (ReconnectionFailedEvent != null)
                              ReconnectionFailedEvent(this, new EventArgs());
                          return false;
                      }
                  }
                  return true;
              }
       
              //釋放資源
              public void Close()
              {
                  _socket.Close();
              }
       
              //啟動空閑事件觸發(fā)
              public void StartCommIdle()
              {
                  //定時器配置
                  _timer = new Timer(500);
                  _timer.AutoReset = true;
                  _timer.Elapsed += TimerElapsed;
                  _timer.Start();
                  _commIdleCount = 0;
              }
              
              //停止空閑事件觸發(fā)
              public void StopCommIdle()
              {
                  _timer.Elapsed -= TimerElapsed;
                  _timer.Close();
              }
       
              //發(fā)送接收數(shù)據(jù)事件
              public void SendRecvEvent()
              {
                  if (CommRxEvent != null)
                      CommRxEvent();
              }
       
              //發(fā)送超時事件
              public void TimerElapsed(object sender, ElapsedEventArgs e)
              {
                  if (_commIdleCount++ >= COMM_IDLE_TIMES)
                  {
                      if (CommIdleEvent != null)
                          CommIdleEvent();
                      _commIdleCount = 0;
                  }
              }
       
              //發(fā)送數(shù)據(jù)接收實現(xiàn),斷線重連
              public int CommSend(byte[] buffer, int size)
              {
                  int sendSize = 0;
                  try 
                  {
                      sendSize = _socket.Send(buffer, size, SocketFlags.None);
                      _commIdleCount = 0;
                  }
                  catch (SocketException e)
                  {
                      ReconnectStatistics++;
                      _reconnect = true;
                  }
                  return sendSize;
              }
       
              //接收數(shù)據(jù)接口實現(xiàn)
              public int CommReceive(byte[] buffer, int size)
              {
                  return recvBuffer.Read(buffer, size);
              }
       
              //接收數(shù)據(jù)接口實現(xiàn)
              public int CommReceive(CommBuffer buffer)
              {
                  return recvBuffer.CopyTo(buffer);
              }
       
              //接收數(shù)據(jù)線程,使用阻塞方式接收數(shù)據(jù)
              public void ReadFormSocket()
              {
                  int recvNum;
                  byte[] recv = new byte[2048];
                  while(true)
                  {
                      try
                      {
                          recvNum = _socket.Receive(recv, SocketFlags.None);
                      }
                      catch(SocketException e)
                      {
                          recvNum = 0;
                      }
       
                      //網(wǎng)絡(luò)斷開Receive返回0
                      if (recvNum == 0 || _reconnect == true)
                      {
                          //重連次數(shù)用盡,退出
                          if (Reconnect() == false)
                              break;
                      }
                      else
                      {
                          recvBuffer.Write(recv, recvNum);
                          SendRecvEvent();
                      }
                      _commIdleCount = 0;
                  }
              }
          }
       
          //通訊緩沖結(jié)構(gòu)類
          internal class CommBuffer
          {
              public uint capacity;              //緩沖區(qū)大小
              public int readPtr;                //讀指針
              public int writePtr;               //寫指針
              public byte[] pBuf;                //緩沖區(qū)
       
              //構(gòu)造函數(shù)
              public CommBuffer(uint capacity)
              {
                  this.capacity = capacity;
                  this.readPtr = 0;
                  this.writePtr = 0;
                  this.pBuf = new byte[capacity];
              }
       
              //從緩沖區(qū)中讀取數(shù)據(jù)到byte數(shù)組
              public int Read(byte[] buff, int size)
              {
                  int readSize;
                  for (readSize = 0; readSize < size; readSize++)
                  {
                      if (IsEmpty())
                          break;
                      buff[readSize] = pBuf[readPtr++];
                      if (readPtr >= capacity)
                          readPtr = 0;
                  }
                  return readSize;
              }
       
              //將byte數(shù)組寫入緩沖區(qū)
              public int Write(byte[] buff, int size)
              {
                  int writeSize, wp;
                  for (writeSize = 0; writeSize < size; writeSize++)
                  {
                      wp = writePtr + 1;
                      if (wp >= capacity)
                          wp = 0;
                      if (wp == readPtr)
                          break;
                      pBuf[writePtr] = buff[writeSize];
                      writePtr = wp;
       
                  }
                  return writeSize;
              }
       
              //拷貝buffer緩沖區(qū)數(shù)據(jù)到本緩沖區(qū)內(nèi),會引起拷貝源有效數(shù)據(jù)為空
              public int Copy(CommBuffer buffer, int len)
              {
                  if (len == 0)
                      return 0;
                  byte[] data = new byte[len];
                  len = buffer.Read(data, len);
                  return this.Write(data, len);
              }
       
              //拷貝本緩沖區(qū)中的數(shù)據(jù)到buffer中,會引起拷貝源有效數(shù)據(jù)為空
              public int CopyTo(CommBuffer buffer)
              {
                  int dataLen = this.DataLength();
                  if (dataLen == 0)
                      return 0;
                  byte[] data = new byte[dataLen];
                  dataLen = this.Read(data, dataLen);
                  return buffer.Write(data, dataLen);
              }
       
              //索引器實現(xiàn)
              public byte this[int index]
              {
                  get
                  {
                      if (index >= DataLength())
                          return 0;
                      int rp = readPtr + index;    
                      if (rp >= capacity)
                          rp = rp-(int)capacity;                
                      return pBuf[rp];
                  }
              }
       
              //忽略len長度的數(shù)據(jù)
              public void Skip(int len)
              {
                  while (len-- > 0) {
                      if (readPtr == writePtr)
                          break;
                      readPtr++;
                      if (readPtr >= capacity)
                          readPtr = 0;
                  }
              }
       
              //判斷是否有數(shù)據(jù)
              public bool IsEmpty()
              {
                  return writePtr == readPtr;
              }
       
              //獲取有效數(shù)據(jù)長度
              public int DataLength()
              {
                  int len = writePtr-readPtr;
                  if (len < 0)
                      len = len + (int)capacity;
                  return len;
              }
       
              //清空緩沖區(qū)
              public void Clear()
              {
                  writePtr = readPtr = 0;
              }
       
              //緩沖區(qū)整理,將數(shù)據(jù)移動到0位置,方便后續(xù)數(shù)據(jù)的處理
              public void Neaten()
              {
                  uint i, j;
       
                  if (readPtr == 0)
                  {
                      return; //讀指針已經(jīng)為0
                  }
       
       
                  if (readPtr >= writePtr)
                  {
                      readPtr = writePtr = 0;
                      return;
                  }
       
       
                  if (writePtr >= capacity)
                  {
                      readPtr = 0;
                      writePtr = 0;
                      return;
                  }
       
                  i = 0;
                  j = (uint)readPtr;
                  while (j < writePtr)
                  {
                      pBuf[i++] = pBuf[j++];
                  }
       
                  readPtr = 0;
                  writePtr = (int)i;
              }
              //顯示緩存數(shù)據(jù)
              public override string ToString()
              {
                  int dataLen = this.DataLength();
                  string str = string.Format("數(shù)據(jù)長度{0}: ", this.DataLength());
                  for (int i = 0; i < dataLen; i++)
                  {
                      str += string.Format("{0:X} ",this[i]);
                  }
                  return str;
              }
          }
      }
      ————————————————
      ps:通訊接口ICommunication用于將各種通訊方式(網(wǎng)絡(luò),232串口,485總線等)的接口統(tǒng)一,如此上層應(yīng)用調(diào)用統(tǒng)一接口即可,無需關(guān)心實際的通訊介質(zhì)。對于單獨的TCP客戶端而言,此處可以跳過,并無實際意義,不喜歡的小伙伴可自行去掉。
      (責(zé)任編輯:admin)本文地址:http://m.bmm520.net/info/net/2021/0711/22472.html

      推薦資訊

      久久精品国产亚洲av麻豆蜜芽 | 亚洲一级片免费看| 亚洲午夜无码久久| 亚洲乱码在线播放| 久久亚洲中文字幕精品有坂深雪 | 亚洲а∨天堂久久精品9966| 亚洲精品亚洲人成在线播放| 亚洲一级毛片中文字幕| 亚洲不卡在线观看| 亚洲一区二区久久| 国产精品亚洲精品青青青| 亚洲剧情在线观看| 77777午夜亚洲| 亚洲精品综合在线影院| 中文字幕在线观看亚洲日韩| 中文无码亚洲精品字幕| 亚洲精品女同中文字幕| 亚洲AV成人精品一区二区三区| 亚洲av无码一区二区三区人妖 | 自拍偷自拍亚洲精品偷一| 亚洲AV日韩AV一区二区三曲| 国产亚洲精品2021自在线| 亚洲精品尤物yw在线影院| 超清首页国产亚洲丝袜| 亚洲乱码一区二区三区在线观看 | 亚洲av永久无码精品国产精品| 亚洲av午夜福利精品一区人妖| 亚洲av无码不卡一区二区三区| 亚洲国产精品人久久| 亚洲黄色网站视频| 亚洲ts人妖网站| 亚洲日本VA午夜在线影院| 亚洲a∨无码精品色午夜| 亚洲国产综合久久天堂| 亚洲最大AV网站在线观看| 亚洲阿v天堂在线| 亚洲精品视频在线播放| 亚洲愉拍一区二区三区| 亚洲 另类 无码 在线| 国产国拍亚洲精品福利| 久久精品国产精品亚洲色婷婷 |