zoukankan      html  css  js  c++  java
  • Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。

      有段时间没有更博了,刚好最近在做Socket通信的项目,原理大致内容:【二维码-(加logo)】-->提供主机地址和端口号信息(直接使用【ThoughtWorks.QRCode.dll】比较简单就不赘述了,核心方法直接贴出来)。然后使用手机APP扫描进行连接服务器,然后通过TCP/IP协议进行握手传输,接收到的图片按照一定的规则进行排列。实时使用心跳包进行检测,服务器进行实时响应。

    一、二维码+logo核心方法:

    引用的命名空间是:using ThoughtWorks.QRCode.Codec;随便用一个控件(比如:Image就可以show出来!)

     1  /// <summary>
     2         /// 初始化二维码对象并根据传入的信息进行创建
     3         /// </summary>
     4         public Bitmap Inilized_QRCode(string Imformation)
     5         {
     6             encoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;//编码方式(注意:BYTE能支持中文,ALPHA_NUMERIC扫描出来的都是数字)
     7             encoder.QRCodeScale = 10;//大小(值越大生成的二维码图片像素越高)
     8             encoder.QRCodeVersion = 0;//版本(注意:设置为0主要是防止编码的字符串太长时发生错误)
     9             encoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;//错误效验、错误更正(有4个等级)
    10             Bitmap bp = encoder.Encode(Imformation, Encoding.GetEncoding("GB2312"));//进行位图编码
    11             Image image = bp;
    12             return bp;
    13         }
    二维码+logo

    二、重点分析心跳包与握手协议:

    本次采用的是Socket进行异步传输,首先要定义服务器地址和端口号(区分网路上其他主机的唯一标识);开始之前先申明:本文采用的机制是一个客户端只使用一个Socket,服务器通过端口进行监听,并发响应客户端。

    服务器

    A、定义Socket 和 获取 监听服务器本机地址 端口号

    1  //监听初始化
    2         private Socket listener = null;//连接客户端
    3         private Socket RemoteClient = null;//接收消息
    4 
    5         private int port = 8089;
    6         private IPEndPoint listenEP = null;

    B、定义好之后开始初始化数据 ,使用AcceptCallback 进行消息回调并等待客户端触发接收数据

     1   private void RunsSocketServer()
     2         {
     3             listener = new Socket(
     4                  AddressFamily.InterNetwork,
     5                  SocketType.Stream,
     6                  ProtocolType.Tcp);
     7             listener.Bind(listenEP);
     8             listener.Listen(10);
     9             listener.BeginAccept(
    10                 new AsyncCallback(AcceptCallback),
    11                 null);
    12 
    13             LB_TXT.Content = "监听已开启" + listener.LocalEndPoint.ToString() + "";
    14 
    15         }

    C、回调函数

     1  private void AcceptCallback(IAsyncResult iar)
     2         {
     3             try
     4             {
     5                 RemoteClient = listener.EndAccept(iar);
     6                 //同进程上的线程异步
     7                 this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
     8                 {
     9                     LB_TXT.Content = string.Format("{0}已连接到服务器", RemoteClient.RemoteEndPoint.ToString());
    10                 });
    11 
    12                 ReceivePicHandShakes();//握手协议及接收图片
    13 
    14             }
    15             catch (Exception err)
    16             {
    17                 this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
    18                  {
    19                      LB_TXT.Content = err.Message;
    20                  });
    21                 return;
    22             }
    23         }

    D、通过开辟一个新的线程进行异步接收数据(图中的协议使用XXX代替,请大家根据自己的协议需要进行定义)

      1   Thread _timers = null;
      2         private Socket _SocketClient = null;
      3         byte[] RecvBytes = new byte[50];
      4         private int Numimgs = 0;//记录接收成功的张数
      5         //byte[] ReImgBytes = new byte[8];//接收图片的头字节
      6         public event ReceivedBitmapHandler ReceivedBitmap;
      7         private delegate void RestartThread();//定义委托
      8 
      9 
     10         public TransferHandler(Socket client)
     11         {
     12             _SocketClient = client;
     13         }
     14 
     15         #region 服务器端
     16         public void BeginReceive()
     17         {
     18             //采用线程直接接收的方式
     19             _timers = new Thread(StartToReceive);
     20             _timers.IsBackground = true;
     21             _timers.Start();
     22         }
     23         /// <summary>
     24         /// 开始接收消息
     25         /// </summary>
     26         private void StartToReceive()
     27         {
     28             //由于long占8位字节,所以先获取前8位字节数据
     29             IAsyncResult iar = _SocketClient.BeginReceive(
     30                 RecvBytes,
     31                 0,
     32                 RecvBytes.Length,
     33                 SocketFlags.None,
     34                 null,
     35                 null);
     36             int len = _SocketClient.EndReceive(iar);
     37             string ReceivemMsg = Encoding.ASCII.GetString(RecvBytes, 0, len);
     38             if (ReceivemMsg.IndexOf("xxx") > 0)//区分业务消息和心跳检测消息
     39             {
     40                 _SocketClient.Send(Encoding.ASCII.GetBytes("xxx"));//回应心跳包
     41                 RecvBytes = new byte[RecvBytes.Length]; //清空数据
     42                 StartToReceive();//回应心跳包完成之后继续等待接收
     43             }
     44             else if (ReceivemMsg == "xxx")//如果收到这个请求,告诉客户端可以开始发送第一张图片了
     45             {
     46                 string order = "xxx";
     47                 byte[] orderdata = Encoding.ASCII.GetBytes(order);   //把字符串编码为字节
     48                 _SocketClient.Send(orderdata);
     49                 RecvBytes = new byte[8]; //开始接收图片
     50                 StartToReceive();//回应指令后完成之后继续等待接收
     51             }
     52             else if (ReceivemMsg == "xxx")
     53             {
     54                 //停止接收图片
     55                 string order = "xxxx";//通知客户端已经完成接收这次所有图片,结束传输;
     56                 byte[] orderdata = Encoding.ASCII.GetBytes(order);   //把字符串编码为字节
     57                 _SocketClient.Send(orderdata);
     58                 StartToReceive();
     59             }
     60             else
     61             {
     62                 int offset = 0;
     63                 int length = BitConverter.ToInt32(RecvBytes, offset);  //先获取文件长度
     64                 ReceiveFile(length);
     65             }
     66 
     67         }
     68 
     69 
     70         public void ReceiveFile(long filelen)
     71         {
     72             MemoryStream ms = new MemoryStream();
     73             int bytesRead = 0;
     74             long count = 0;
     75             byte[] buffer = new byte[8192];
     76 
     77             while (count != filelen)
     78             {
     79                 try
     80                 {
     81                     bytesRead = _SocketClient.Receive(buffer);
     82                     ms.Write(buffer, 0, bytesRead);
     83                     count += bytesRead;
     84                 }
     85                 catch (Exception ex)
     86                 {
     87 
     88                 }
     89             }
     90             ReceivedBitmap(new Bitmap(ms));
     91 
     92 
     93             //接收完成之后清空数据,继续接收
     94             buffer = new byte[buffer.Length];//缓存
     95             RecvBytes = new byte[RecvBytes.Length];//用于接收
     96 
     97             string order = "xxxx";//通知客户端已经收到图片,请继续
     98             byte[] orderdata = Encoding.ASCII.GetBytes(order);   //把字符串编码为字节
     99             _SocketClient.Send(orderdata);
    100             Numimgs++;
    101 
    102             if (Numimgs >= 3)
    103             {
    104                 RecvBytes = new byte[50];
    105                 Numimgs = 0;
    106             }
    107             StartToReceive(); //接收完成之后继续接收
    108         }
    109         #endregion

    * 客户端

    A、心跳包发送(代码中XXX解释同上)

     1         #region 心跳检测
     2         private void HeartBeatsTests()
     3         {
     4             Thread sendEcho = new Thread(new ThreadStart(socketSend));
     5             sendEcho.Start();
     6             sendEcho.IsBackground = true;
     7         }
     8 
     9 
    10         public void socketSend()
    11         {
    12             while (true)
    13             {
    14                 Thread.Sleep(10000);//每十秒发一次,响应发3次
    15                 //备注:IsSendingImgs必须放在sleep底下,防止等待的空余时间发送导致服务端继续接收心跳包
    16                 if (!IsSendingImgs)//如果是任务还在执行不允许第二次发送,必须要等待完成
    17                 {
    18                     try
    19                     {
    20                         this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
    21                         {
    22                             this.richTextBox.AppendText("开始发送心跳包..." + Environment.NewLine);
    23                         });
    24                         client.Send(Encoding.ASCII.GetBytes("xxxx"));//发送心跳暗号
    25                     }
    26                     catch (SocketException)
    27                     {
    28                         Thread.CurrentThread.Abort();
    29                         //throw ex;
    30                     }
    31                     //标记发送的次数
    32                     this.MyLostTime++;
    33                     //如果外发了3次请求暗号后仍不见服务器的回应,则认为客户端已经与服务器断开联系了
    34                     if (this.MyLostTime >= 3)
    35                     {
    36                         //IsSendingImgs = true;
    37                         TimeSpan t = DateTime.Now - lastConnect;
    38                         if (t.TotalSeconds > 30)
    39                         {
    40                             this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
    41                             {
    42                                 //this.richTextBox.cont
    43                                 this.richTextBox.AppendText("与服务器失去联系..." + Environment.NewLine);
    44                             });
    45                         }
    46                         else
    47                         {
    48 
    49                             this.countnum++;
    50                             this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
    51                             {
    52                                 //this.richTextBox.cont
    53                                 this.richTextBox.AppendText("服务器响应第" + countnum.ToString() + "次,响应时间是:" + DateTime.Now.ToString() + "" + Environment.NewLine);
    54                             });
    55                         }
    56                         //每次确定后重置
    57                         this.MyLostTime = 0;
    58                     }
    59                 }
    60                 else
    61                 {
    62                     lastConnect = DateTime.Now;//每十秒刷新,防止图片传送任务完成之后导致时间错误;
    63                 }
    64             }
    65 
    66         }
    67 
    68         #endregion

    B、响应机制(发送图片及接收消息触发停止、开启心跳包代码中XXX解释同上)

     1         /// <summary>
     2         /// 协议请求
     3         /// </summary>
     4         public void SendSocketData()
     5         {
     6             string sendStr = "xxx"; //向服务器发送请求:发送3张图片的指令
     7             byte[] buffer = Encoding.ASCII.GetBytes(sendStr);   //把字符串编码为字节
     8             _SendQuest.Send(buffer); //发送
     9             _SendQuest.BeginReceive(Rbuffer, 0, Rbuffer.Length, 0, new AsyncCallback(CallbackRuquest), null);//等待接收并回调
    10         }
    11 
    12 
    13         /// <summary>
    14         /// 回调告诉socket可以执行下一步了
    15         /// </summary>
    16         /// <param name="iar"></param>
    17         private void CallbackRuquest(IAsyncResult arr)
    18         {
    19             int aa = _SendQuest.EndReceive(arr);
    20             string Rc_Msg = Encoding.ASCII.GetString(Rbuffer, 0, aa);
    21             if (Rc_Msg == "xxx")
    22             {
    23                 khd.IsSendingImgs = false;//启动心跳包--用于接收完成之后继续开启心跳包用
    24                 Numimgs = 0;
    25                 _SendQuest.BeginReceive(Rbuffer, 0, Rbuffer.Length, 0, new AsyncCallback(CallbackRuquest), null);//等待接收并回调
    26             }
    27             else
    28             {
    29                 //结束接收
    30                 if (Numimgs >= 3)
    31                 {
    32                     //结束
    33                     byte[] buffer = Encoding.ASCII.GetBytes("xxx");
    34                     _SendQuest.Send(buffer);
    35                     _SendQuest.BeginReceive(buffer, 0, buffer.Length, 0, new AsyncCallback(CallbackRuquest), null);//等待接收并回调
    36                   
    37                 }
    38                 else
    39                 {
    40                     //接收成功并进行回调
    41 
    42                     if (Rc_Msg.IndexOf("xxx") > 0)
    43                     {
    44                         Socket_Client.lastConnect = DateTime.Now;//重新赋值
    45                         khd.Dispatcher.BeginInvoke((MethodInvoker)delegate ()
    46                         {
    47                             khd.richTextBox.AppendText("接收到服务器的响应消息" + Environment.NewLine);
    48                         });
    49                         _SendQuest.BeginReceive(Rbuffer, 0, Rbuffer.Length, 0, new AsyncCallback(CallbackRuquest), null);//等待接收并回调
    50                     }
    51                     else if (Rc_Msg == "xxx" || Rc_Msg == "xxx")
    52                     {
    53                         FileInfo fi = new FileInfo(Filename);//获取文件
    54                         byte[] len = BitConverter.GetBytes(fi.Length);//文件数据
    55                                                                       //首先把文件长度发送过去
    56                         _SendQuest.BeginSendFile(Filename, len, null, TransmitFileOptions.UseDefaultWorkerThread, new AsyncCallback(EndSendback), null);
    57                         _SendQuest.BeginReceive(Rbuffer, 0, Rbuffer.Length, 0, new AsyncCallback(CallbackRuquest), null);//等待接收并回调
    58                                                                                                                          //清空数据
    59                         len = new byte[fi.Length];
    60                         Numimgs++;
    61                     }
    62                 }
    63             }
    64         }
    65 
    66         /// <summary>
    67         /// 结束发送 
    68         /// </summary>
    69         /// <param name="eof"></param>
    70         private void EndSendback(IAsyncResult eof)
    71         {
    72             _SendQuest.EndSendFile(eof);
    73         }
    74 
    75         /// <summary>
    76         /// 结束接收 
    77         /// </summary>
    78         /// <param name="eof"></param>
    79         private void EndCallback(IAsyncResult eof)
    80         {
    81             //结束接收
    82             _SendQuest.EndReceive(eof);
    83 
    84         }

    这边只着重做出如何响应如何发送的逻辑实现,做个笔记。还是蛮有意思的。以下是Demo效果图:

    此文的Demo下载地址:Socket心跳通信

  • 相关阅读:
    nginx 配置https详细步骤
    Git 上传本地仓库到远程git仓库
    VUE 配置vscode关于vue插件
    ORA-01439:要更改数据类型,则要修改的列必须为空
    Oracle查看主键、删除主键、添加联合主键
    std::stoi, std::stol, std::stoll
    C+++string类如何判断字符串为空
    1day漏洞反推技巧实战(1)
    java反射笔记,自用
    tomcat Valve内存马
  • 原文地址:https://www.cnblogs.com/BarryJames/p/7161791.html
Copyright © 2011-2022 走看看