Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。

来源:互联网 发布:颜值高的游戏本知乎 编辑:程序博客网 时间:2024/06/03 21:37

  有段时间没有更博了,刚好最近在做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                     try19                     {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.cont43                                 this.richTextBox.AppendText("与服务器失去联系..." + Environment.NewLine);44                             });45                         }46                         else47                         {48 49                             this.countnum++;50                             this.Dispatcher.BeginInvoke((MethodInvoker)delegate ()51                             {52                                 //this.richTextBox.cont53                                 this.richTextBox.AppendText("服务器响应第" + countnum.ToString() + "次,响应时间是:" + DateTime.Now.ToString() + "" + Environment.NewLine);54                             });55                         }56                         //每次确定后重置57                         this.MyLostTime = 0;58                     }59                 }60                 else61                 {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             else28             {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                 else39                 {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心跳通信

 

阅读全文
0 0
原创粉丝点击