• 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

  • 相关阅读:
    leetcode| Intersection of Two Arrays II
    Spring Boot起步依赖:定制starter
    SpringBoot自动配置的魔法是怎么实现的
    Dubbo中的IoC实现
    必须知道的String知识点
    Dubbo的SPI机制
    为什么要设置HTTP timeout?
    重构代码——简单工厂模式+模板方法模式
    计算机基础——位运算
    系统间HTTP调用代码封装
  • 原文地址:https://www.cnblogs.com/liyijin/p/8906687.html
Copyright © 2020-2023  润新知