1. 程式人生 > >c#之異步Socket通信

c#之異步Socket通信

tex BE 多個 .sh interval 異步 gets 全局變量 AC

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

一.服務端

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

//新建一個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,說明此次數據接收完畢。

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()方法返回成功發送的字節數量

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

二.客戶端

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

// 新建客戶端實例,並連接到服務端所在的端口號
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 發送與接收數據(與客戶端類似)

三.運行示例

技術分享圖片

四.總結

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

其中需要註意的地方如下:

4.1.在異步接收數據的時候,因為接收到的數據大小是不確定的,所以暫定每次只接收1024字節,根據Available判斷,如果接收到的數據大於1024字節,則依次拼接得到的數據。

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

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

c#之異步Socket通信