c#之異步Socket通訊

0.基於上一篇的c#之Socket(同步)通訊,在幾個大神評論以後,發現是有挺多地方不足的,因此寫了一個改進版本的基於c#的異步Socket通訊。再加深一下對Socket的使用和理解。其中客戶端和服務端均採用WPF界面,實現了心跳,斷線重連,一個服務端對應多個客戶端的功能。c#

一.服務端異步

1.1 先建立一個Socket實例,並綁定到20000端口號;經過Listen方法開始監聽並設置最大監聽數量。socket

//新建一個Socket服務端實例,並綁定到20000端口
this.socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.socketServer.Bind(new IPEndPoint(IPAddress.Any, 20000)); //設置最大監聽數量
this.socketServer.Listen(10);

1.2 開始異步監聽客戶端,使用的是BeginAccept與EndAccept,當有客戶端鏈接後會自動調用回調函數,此時開始心跳並將客戶端的Socket與心跳等信息加入到全局字典中去。函數

this.socketServer.BeginAccept(ar => { Socket _socket = socketServer.EndAccept(ar); //開始心跳
    System.Timers.Timer heartbeatTimer = new System.Timers.Timer(); heartbeatTimer.Interval = 600000; heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler((s, e) => heartbeatTimerIsUp(s, e, _socket)); heartbeatTimer.Start(); SocketInfo info = new SocketInfo(); info.heartbeatTimer = heartbeatTimer; this.socketInfoDic.Add(_socket, info); //開始接收數據
 thdRevMethod(_socket); //開始下一次監聽
 ListenSocket(); }, null);

1.3 接收數據,當上一步中有客戶端鏈接成功後,便可開啓異步接收來自客戶端的數據;使用的是BeginReceive與EndReceive,當接收到來自客戶端的數據後,會自動調用回調函數。因爲接收到的數據的大小不肯定,因此這裏每次接收1024字節,而後將接收到的數據拼接起來,用到Available 屬性,爲可讀取的字節數,若是小於等於0,說明這次數據接收完畢。this

Socket _socket = obj as Socket; if (this.socketInfoDic.ContainsKey(_socket)) { SocketInfo socketInfo = socketInfoDic[_socket]; //開始接收消息
    _socket.BeginReceive(socketInfo.tempByte, 0, socketInfo.tempByte.Length, SocketFlags.None, ar => { try { int resInt = _socket.EndReceive(ar); socketInfo.contentByte = socketInfo.contentByte.Concat(socketInfo.tempByte).ToArray(); socketInfo.tempByte = new byte[1024]; if (_socket.Available <= 0) { int actualLength = socketInfo.contentByte.Length - (socketInfo.tempByte.Length - resInt); string res = Encoding.Default.GetString(socketInfo.contentByte, 0, actualLength); socketInfo.contentByte = new byte[0]; } thdRevMethod(_socket); } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.ConnectionReset) { //當客戶端斷開鏈接,從列表中移除該客戶端
            if (this.socketInfoDic.ContainsKey(_socket)) { this.socketInfoDic.Remove(_socket); } } } catch (Exception ex) { MessageBox.Show("程序出現異常:" + ex); } }, null); }

1.4 發送數據,用到的是BeginSend與EndSend,發送成功後會自動調用回調函數,其中EndSend()方法返回成功發送的字節數量spa

byte[] byteStr = Encoding.Default.GetBytes(msg); _socket.BeginSend(byteStr, 0, byteStr.Length, SocketFlags.None, ar => { _socket.EndSend(ar); }, null);

二.客戶端code

2.1 新建Socket實例並開始異步鏈接到服務端,用到的是BeginConnect與EndConnect,鏈接成功會自動調用回調函數,此時可開始心跳,接收發送數據。blog

// 新建客戶端實例,並鏈接到服務端所在的端口號
this.socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //鏈接到服務端
this.socketClient.BeginConnect("127.0.0.1", 20000, ar => { try { this.socketClient.EndConnect(ar); this.thdRevMethod(); } catch (SocketException sex) { if (sex.SocketErrorCode == SocketError.ConnectionRefused) { this.reconnectTimer.Start(); this.heartbeatTimer.Stop(); } } catch (Exception) { this.heartbeatTimer.Stop(); throw; } }, null);

2.2 發送與接收數據(與客戶端相似)get

三.運行示例回調函數

四.總結

假如上述描述,或者代碼邏輯中有任何問題,但願各位大神幫忙指出來,謝謝!

其中須要注意的地方以下:

4.1.在異步接收數據的時候,由於接收到的數據大小是不肯定的,因此暫定每次只接收1024字節,根據Available判斷,若是接收到的數據大於1024字節,則依次拼接獲得的數據。

4.2 在服務端中,每次接收到1024字節的byte[]不能定義成全局變量,而須要與每一個Socket客戶端一一對應,不然再接收來自多個客戶端的消息時會出錯。(這邊我理解是這樣)

源碼下載地址以下:AsyncSocketDemo.rar

相關文章
相關標籤/搜索