c#Socket服务端异步高性能并发 解决 半包 粘包问题 封装类

/// 
/// Socket连接,双向通信
/// 
public class SocketConnection
{
    #region 构造函数

    public SocketConnection(Socket socket, SocketServer server)
    {
        _socket = socket;
        _server = server;
    }

    #endregion

    public Socket ClientSocket { get { return _socket; } }
    public bool IsCheck { get; set; }//该客户是否通过首次验证  否则无法进行后续操作
    #region 私有成员

    private readonly Socket _socket;
    private bool _isRec = true;
    private SocketServer _server = null;
    private byte[] Tmp_byteArr = new byte[0];//缓存 多余 或者 不完整 的封包数据
    private bool IsSocketConnected()
    {
        bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
        bool part2 = (_socket.Available == 0);
        if (part1 && part2)
            return false;
        else
            return true;
    }

    #endregion

    #region 外部接口

    /// 
    /// 开始接受客户端消息
    /// 
    public void StartRecMsg()
    {
        try
        {
            byte[] container = new byte[1024 * 1024 * 2];
            _socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
            {
                try
                {
                    int length = _socket.EndReceive(asyncResult);

                    //马上进行下一轮接受,增加吞吐量
                    if (length > 0 && _isRec && IsSocketConnected())
                        StartRecMsg();

                    if (length > 0)
                    {
                        byte[] recBytes = new byte[length];
                        Array.Copy(container, 0, recBytes, 0, length);
                        ParMsg(recBytes);

                        ////处理消息 这里我把他取消了 放到了下面消息解析里面触发 当解析出完整数据时再触发 这样订阅事件的地方不用在做处理
                        //HandleRecMsg?.Invoke(recBytes, this, _server);
                    }
                    else
                        Close();
                }
                catch (Exception ex)
                {
                    HandleException?.Invoke(ex);
                    Close();
                }
            }, null);
        }
        catch (Exception ex)
        {
            HandleException?.Invoke(ex);
            Close();
        }
    }

    void ParMsg(byte[] arr)
    {
        List result = new List();//之后所有的操作数据都保存在这  list方便点
        if (Tmp_byteArr.Length > 0)//判断之前是否有保存多余数据
        {
            result.AddRange(Tmp_byteArr);
        }
        result.AddRange(arr);
        if (result.Count <= 4)//前4个字节代表包长 如果不够则表示包不完整 保存起来
        {
            Tmp_byteArr = result.ToArray();
            return;
        }
        int DataLength = BitConverter.ToInt32(Common.SubByte(result.ToArray(), 0, 4), 0);//包长 但不含这个包长本身
        if (DataLength == result.Count - 4)//如果相等 则刚好是一个完整的包
        {
            MsgBody msg = new MsgBody(result.ToArray());
            //处理消息 
            HandleRecMsg?.Invoke(msg, this, _server);
            return;
        }
        if (DataLength > result.Count - 4)//如果包长 大于实际消息长度 说明包不完整 则保存至下次使用
        {
            Tmp_byteArr = result.ToArray();
            return;
        }
        if (DataLength < result.Count - 4)//如果包长 小于实际消息长度 说明存在了粘包 要把多余的内容递归使用
        {
            MsgBody msg = new MsgBody(result.Take(DataLength).ToArray());
            //处理消息 
            HandleRecMsg?.Invoke(msg, this, _server);
            ParMsg(result.Skip(DataLength).ToArray());
        }

    }

    /// 
    /// 发送数据
    /// 
    /// 数据字节
    public void Send(byte[] bytes)
    {
        try
        {
            _socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
            {
                try
                {
                    int length = _socket.EndSend(asyncResult);
                    HandleSendMsg?.Invoke(bytes, this, _server);
                }
                catch (Exception ex)
                {
                    HandleException?.Invoke(ex);
                }
            }, null);
        }
        catch (Exception ex)
        {
            HandleException?.Invoke(ex);
        }
    }

    /// 
    /// 发送字符串(默认使用UTF-8编码)
    /// 
    /// 字符串
    public void Send(string msgStr)
    {
        Send(Encoding.UTF8.GetBytes(msgStr));
    }

    /// 
    /// 发送字符串(使用自定义编码)
    /// 
    /// 字符串消息
    /// 使用的编码
    public void Send(string msgStr, Encoding encoding)
    {
        Send(encoding.GetBytes(msgStr));
    }

    /// 
    /// 传入自定义属性
    /// 
    public object Property { get; set; }

    /// 
    /// 关闭当前连接
    /// 
    public void Close()
    {
        try
        {
            _isRec = false;
            _socket.Disconnect(false);
            _server.ClientList.Remove(this);
            HandleClientClose?.Invoke(this, _server);
            _socket.Close();
            _socket.Dispose();
            GC.Collect();
        }
        catch (Exception ex)
        {
            HandleException?.Invoke(ex);
        }
    }

    #endregion

    #region 事件处理

    /// 
    /// 客户端连接接受新的消息后调用
    /// 
    public Action HandleRecMsg { get; set; }

    /// 
    /// 客户端连接发送消息后回调
    /// 
    public Action HandleSendMsg { get; set; }

    /// 
    /// 客户端连接关闭后回调
    /// 
    public Action HandleClientClose { get; set; }

    /// 
    /// 异常处理程序
    /// 
    public Action HandleException { get; set; }

    #endregion
}


/// 
/// Socket服务端
/// 
public class SocketServer
{
    #region 构造函数

    /// 
    /// 构造函数
    /// 
    /// 监听的IP地址
    /// 监听的端口
    public SocketServer(string ip, int port)
    {
        _ip = ip;
        _port = port;
    }

    /// 
    /// 构造函数,监听IP地址默认为本机0.0.0.0
    /// 
    /// 监听的端口
    public SocketServer(int port)
    {
        _ip = "0.0.0.0";
        _port = port;
    }

    #endregion

    #region 内部成员

    private Socket _socket = null;
    private string _ip = "";
    private int _port = 0;
    private bool _isListen = true;
    private void StartListen()
    {
        try
        {
            _socket.BeginAccept(asyncResult =>
            {
                try
                {
                    Socket newSocket = _socket.EndAccept(asyncResult);

                    //马上进行下一轮监听,增加吞吐量
                    if (_isListen)
                        StartListen();

                    SocketConnection newClient = new SocketConnection(newSocket, this)
                    {
                        HandleRecMsg = HandleRecMsg == null ? null : new Action(HandleRecMsg),
                        HandleClientClose = HandleClientClose == null ? null : new Action(HandleClientClose),
                        HandleSendMsg = HandleSendMsg == null ? null : new Action(HandleSendMsg),
                        HandleException = HandleException == null ? null : new Action(HandleException)
                    };

                    newClient.StartRecMsg();
                    ClientList.AddLast(newClient);

                    HandleNewClientConnected?.Invoke(this, newClient);
                }
                catch (Exception ex)
                {
                    HandleException?.Invoke(ex);
                }
            }, null);
        }
        catch (Exception ex)
        {
            HandleException?.Invoke(ex);
        }
    }

    #endregion

    #region 外部接口

    /// 
    /// 开始服务,监听客户端
    /// 
    public void StartServer()
    {
        try
        {
            //实例化套接字(ip4寻址协议,流式传输,TCP协议)
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //创建ip对象
            IPAddress address = IPAddress.Parse(_ip);
            //创建网络节点对象包含ip和port
            IPEndPoint endpoint = new IPEndPoint(address, _port);
            //将 监听套接字绑定到 对应的IP和端口
            _socket.Bind(endpoint);
            //设置监听队列长度为Int32最大值(同时能够处理连接请求数量)
            _socket.Listen(int.MaxValue);
            //开始监听客户端
            StartListen();
            HandleServerStarted?.Invoke(this);
        }
        catch (Exception ex)
        {
            HandleException?.Invoke(ex);
        }
    }

    /// 
    /// 所有连接的客户端列表
    /// 
    public LinkedList ClientList { get; set; } = new LinkedList();

    /// 
    /// 关闭指定客户端连接
    /// 
    /// 指定的客户端连接
    public void CloseClient(SocketConnection theClient)
    {
        theClient.Close();
    }

    #endregion

    #region 公共事件

    /// 
    /// 异常处理程序
    /// 
    public Action HandleException { get; set; }

    #endregion

    #region 服务端事件

    /// 
    /// 服务启动后执行
    /// 
    public Action HandleServerStarted { get; set; }

    /// 
    /// 当新客户端连接后执行
    /// 
    public Action HandleNewClientConnected { get; set; }

    /// 
    /// 服务端关闭客户端后执行
    /// 
    public Action HandleCloseClient { get; set; }

    #endregion

    #region 客户端连接事件

    /// 
    /// 客户端连接接受新的消息后调用
    /// 
    public Action HandleRecMsg { get; set; }

    /// 
    /// 客户端连接发送消息后回调
    /// 
    public Action HandleSendMsg { get; set; }

    /// 
    /// 客户端连接关闭后回调
    /// 
    public Action HandleClientClose { get; set; }

    #endregion
}

public class MsgBody
{
public MsgBody()
{

    }
    public MsgBody(string str)
    {
        this.BodyData = str;
    }
    public MsgBody(byte[] b)
    {
        this.Source = b;
        SetValues();
    }

    /// 
    /// 包长
    /// 
    public int DataLength;
    public byte[] DataLength_Byte;

    /// 
    /// 包长
    /// 
    public int DataLength_2;
    public byte[] DataLength_2_Byte;

    /// 
    /// 命令字 用来标记消息类型
    /// 
    public int Cmd;
    public byte[] Cmd_Byte;

    /// 
    /// 发生的文本内容
    /// 
    public string BodyData;
    public byte[] BodyData_Byte;

    public byte End = 0;

    public byte[] Source;

    /// 
    /// 数据组合为byte数组
    /// 
    /// 
    public byte[] ToByteArray()
    {
        BodyData_Byte = Encoding.UTF8.GetBytes(BodyData);
        Cmd_Byte = BitConverter.GetBytes(Cmd);
        DataLength = DataLength_2 = BodyData_Byte.Length + Cmd_Byte.Length + 4 + 1;
        DataLength_Byte = BitConverter.GetBytes(DataLength);
        DataLength_2_Byte = BitConverter.GetBytes(DataLength_2);

        byte[] result = new byte[DataLength + 4];
        Array.Copy(DataLength_Byte, 0, result, 0, 4);
        Array.Copy(DataLength_2_Byte, 0, result, 4, 4);
        Array.Copy(Cmd_Byte, 0, result, 8, 4);
        Array.Copy(BodyData_Byte, 0, result, 12, BodyData_Byte.Length);
        Source = new byte[result.Length];
        Array.Copy(result, 0, Source, 0, result.Length);
        return result;
    }

    /// 
    /// 数据解析
    /// 
    public void SetValues()
    {
        try
        {
            DataLength_Byte = Common.SubByte(Source, 0, 4);
            DataLength_2_Byte = Common.SubByte(Source, 4, 4);
            Cmd_Byte = Common.SubByte(Source, 8, 4);
            BodyData_Byte = Common.SubByte(Source, 12, Source.Length - 13);
            End = Common.SubByte(Source, Source.Length - 2, 1)[0];

            DataLength = BitConverter.ToInt32(DataLength_Byte, 0);
            DataLength_2 = BitConverter.ToInt32(DataLength_2_Byte, 0);
            Cmd = BitConverter.ToInt32(Cmd_Byte, 0);
            BodyData = Encoding.UTF8.GetString(BodyData_Byte);
        }
        catch (Exception ex)
        {

            throw;
        }
    }
}

你可能感兴趣的:(c#Socket服务端异步高性能并发 解决 半包 粘包问题 封装类)