0.雖然以前在項目中也有用過Socket,但始終不是本身搭建的,因此對Server,Clinet端以及心跳,斷線重連總沒有很深刻的理解,如今本身搭建了一遍加深一下理解。socket
服務端使用WPF界面,客戶端使用控制檯。實現了心跳,斷線重連,一個服務端對應多個客戶端的功能。spa
一.服務端線程
1.1 先建立一個Socket實例,並綁定到20000端口號;經過Listen方法開始監聽並設置最大監聽數量。code
//新建一個Socket服務端實例,並綁定到20000端口 socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socketServer.Bind(new IPEndPoint(IPAddress.Any, 20000)); //設置最大監聽數量 socketServer.Listen(10);
1.2 當有客戶端成功鏈接,則會經過Accept方法生產一個新的Socket實例;此時可開啓心跳定時器,並開啓一個線程接收消息。blog
//開啓一個線程監聽客戶端 Thread thd = new Thread(new ThreadStart(ListenSocket)); thd.Start(); /// <summary> /// 開始監聽 /// </summary> private void ListenSocket() { try { while (true) { Socket _socket = socketServer.Accept(); //開始心跳 System.Timers.Timer heartbeatTimer = new System.Timers.Timer(); heartbeatTimer.Interval = 60000; heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler((s, e) => heartbeatTimerIsUp(s, e, _socket)); heartbeatTimer.Start(); Thread thdReceive = new Thread(new ParameterizedThreadStart(thdRevMethod)); thdReceive.Start(_socket); } } catch (Exception ex) { MessageBox.Show("程序出現異常:" + ex); } }
1.3 接收消息與發送消息的代碼以下進程
/// <summary> /// 接收消息 /// </summary> /// <param name="obj"></param> private void thdRevMethod(object obj) { Socket _socket = obj as Socket; try { while (true) { byte[] resByte = new byte[1024]; int resInt = _socket.Receive(resByte); if (resInt > 0) { string res = Encoding.Default.GetString(resByte, 0, resInt);///接收消息後操做 } } } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.ConnectionReset) { //當客戶端斷開鏈接,從客戶端列表中移除該客戶端 } } catch (Exception ex) { MessageBox.Show("程序出現異常:" + ex); } } /// <summary> /// 發送消息 /// </summary> /// <param name="msg"></param> /// <param name="ipAndPord"></param> public bool sendMsg(string msg,string ipAndPord) { try { Socket _socket = socketTimerDic.SingleOrDefault(r => string.Equals(r.Key.RemoteEndPoint.ToString(), ipAndPord)).Key; if (_socket != null) { byte[] byteStr = Encoding.Default.GetBytes(msg); _socket.Send(byteStr); return true; } else { return false; } } catch (Exception) { return false; } }
二.客戶端ip
2.1 先建立一個Socket實例,並鏈接到服務端所在的ip端口;鏈接成功後開啓心跳定會器並開啓發送和接收消息的兩個線程。get
// 新建客戶端實例,並鏈接到服務端所在的端口號 socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socketClient.Connect("127.0.0.1", 20000); Console.WriteLine("成功鏈接到服務端"); heartbeatTimer.Start(); //開啓一個線程發送消息 Thread thdSend = new Thread(new ThreadStart(thdSendMethod)); thdSend.Start(); //開啓一個線程接收信息 Thread thdRev = new Thread(new ThreadStart(thdRevMethod)); thdRev.Start();
2.2 發送消息和接收消息的代碼以下,當服務端斷開會在接收消息的線程中觸發sex.SocketErrorCode == SocketError.ConnectionReset的異常,此時捕獲到後開啓重連定時器便可源碼
/// <summary> /// 接收消息 /// </summary> private static void thdRevMethod() { try { while (true) { byte[] resByte = new byte[1024]; int resInt = socketClient.Receive(resByte); if (resInt > 0) { Console.WriteLine(Encoding.Default.GetString(resByte,0, resInt)); } } } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.ConnectionReset) { Console.WriteLine("服務端斷開!5s後重連!"); reconnectTimer.Start(); heartbeatTimer.Stop(); } } catch (Exception ex) { Console.WriteLine("程序出現異常:" + ex); heartbeatTimer.Stop(); } } //發送信息 private static void thdSendMethod() { try { while (true) { string res = Console.ReadLine(); byte[] resByte = Encoding.Default.GetBytes(res); socketClient.Send(resByte); } } catch (Exception ex) { Console.WriteLine("程序出現異常:" + ex); heartbeatTimer.Stop(); } }
三.運行示例string
四..總結
假如代碼中有錯誤的地方,但願能夠幫忙指出來改之。
其中要注意的地方以下:
1.WPF關閉窗口後,須要經過 Environment.Exit(0);來結束掉當前整個服務端進程,不然Socket開啓的接收和發送進程將還在,客戶端也不會檢測到服務端斷開。
2.在SocketException中捕獲異常sex.SocketErrorCode == SocketError.ConnectionReset時說明客戶端/服務端鏈接斷開了,須要進行重連等操做。
3.在接收到消息時候須要用GetString(byte[] bytes, int index, int count)指定長度,而不應使用GetString(byte[] bytes),不然可能出現不少空格。
4.定時器假如須要傳遞參數,能夠經過設置全局變量,或者(s, e) => heartbeatTimerIsUp(s, e, _socket)單獨傳遞一個變量到方法中。
源碼下載地址以下:SocketDemo.rar