有段時間沒有更博了,剛好最近在做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 }
二、重點分析心跳包與握手協議:
本次採用的是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心跳通信