小小屌丝程序员

导航

c#之异步Socket通信

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

 

posted on 2018-04-22 13:22  小小屌丝程序员  阅读(2472)  评论(3编辑  收藏  举报