易语言hpsocket教程_net socket框架

易语言hpsocket教程_net socket框架一、序言 1、HPSocket.Net介绍 (1)HPSocket是一套高性能TCP/UDP/HTTP通信框架(适用于Windows、Linux和OSX的HP Socket),包括服务器、客户端和代理组件。 (2)它被广泛应用于各种场景中的TCP/UDP/HTTP通信应用。它提供了C/C++、C#、

一、序言

易语言hpsocket教程_net socket框架

1、HPSocket.Net介绍

(1)HPSocket是一套高性能TCP/UDP/HTTP通信框架(适用于Windows、Linux和OSX的HP Socket),包括服务器、客户端和代理组件。
(2)它被广泛应用于各种场景中的TCP/UDP/HTTP通信应用。它提供了C/C++、C#、Delphi、Java、Python等编程语言接口。HP Socket完全封装了通信层,应用程序不需要关注通信层的任何细节。
(3)HPSocket支持的平台:Windows 7+x86/x64、Linux内核2.6.32+x86/x64、mac操作系统10.12+x64、树莓派4B。
(4)HPSocket除了TCP/UDP/HTTP协议,还支持HTTP、WebSocket等协议。
(5)HP-Socket 在设计上充分考虑性能、使用场景、复杂性和易用性等因素,作出以下几点设计决策: Client 组件:基于 Event Select 通信模型,在单独线程中执行通信操作,避免与主线程或其他线程相互干扰。每个组件对象管理一个 Socket 连接。 Server 组件:基于 IOCP 通信模型,并结合缓存池、私有堆(Private Heap)等技术,支持超大规模连接,在高并发场景下实现高效内存管理。 Agent 组件:对于代理服务器或中转服务器等应用场景,服务器自身也作为客户端向其它服务器发起大规模连接,一个 Agent 组件对象同时可管理多个 Socket 连接;Agent 组件与 Server 组件采用相同的技术架构,可以用作代理服务器或中转服务器的客户端部件。

2、HPSocket.Net相对于System.Net.Sockets具有的优势

(1)HPSocket提供了更多的协议支持,如TCP、UDP、HTTP、WebSocket等,而System.Net.Sockets只支持TCP、UDP等基本协议。
(2)HPSocket的性能更高,可以处理更多的并发连接,支持更高的吞吐量和更低的延迟。
(3)HPSocket提供了更多的高级功能,如SSL加密、压缩、心跳包等,而System.Net.Sockets只提供了基本的网络通信功能。
(4)HPSocket比System.Net.Sockets跨平台支持的早,兼容性或许更好。
(5)HPSocket提供了好多的扩展功能,比如:TCP协议处理分包、组包、粘包、维护连接列表等,UDP协议处理丢包、乱序等,可设置Socket通讯的线程、缓存等性能优化相关的配置。
(6)HPSocket作者对TCPServer、TCPClient的事件回调函数做了线程优化,不需要我们再考虑这些问题。

3、与HPSocket差不多的Socket框架

(1)DotNetty(博客有讲)
(2)SuperSocket(可选)

二、基础使用

  代码地址:csharp_networkprotocol_hpsocket

  视频演示:C#-网络通讯框架_HPSocket TCP演示

易语言hpsocket教程_net socket框架

  • TCP基础组件:
    • ITcpServer
    • ITcpAgent
    • ITcpClient
    • ITcpPullServer
    • ITcpPullAgent
    • ITcpPullClient
    • ITcpPackServer(重点;解决TCP粘包问题,需配合ITcpPackClient使用)
    • ITcpPackAgent(重点;Multi-Client组件,与Server组件采用相同的技术架构。可同时建立和高效处理大规模Socket连接。适用于代理,转发,网关等需要建立大量客户端连接的场景)
    • ITcpPackClient(重点;解决TCP粘包问题,需配合ITcpPackServer使用)
  • UDP
    • IUdpServer
    • IUdpClient
    • IUdpCast
    • IUdpArqServer(重点;可靠UDP)
    • IUdpArqClient(重点;可靠UDP)
    • IUdpNode
  • SSL
    • ISslServer
    • ISslAgent
    • ISslClient
    • ISslPullServer
    • ISslPullAgent
    • ISslPullClient
    • ISslPackServer
    • ISslPackAgent
    • ISslPackClient
  • HTTP
    • IHttpServer
    • IHttpsServer
    • IHttpAgent
    • IHttpsAgent
    • IHttpClient
    • IHttpsClient
    • IHttpSyncClient
    • IHttpsSyncClient
  • ThreadPool(HPSocket.Thread.ThreadPool)
    • ThreadPool

1、TCP

  这里我演示TCPServerTCPClient基础类的使用,代码地址:csharp_networkprotocol_hpsocket;在平时使用中,推荐下面两种方案:

  • ITcpServer<TRequestBodyType>ITcpClient<TRequestBodyType>ITcpAgent<TRequestBodyType>”+“TCP数据接收适配器组件(BinaryDataReceiveAdapter)”;
  • ITcpPackServerITcpPackClientITcpPackAgent

(1)TcpServer使用示例

易语言hpsocket教程_net socket框架

 TCPServerOld.Designer.cs

查看代码
 namespace csharp_networkprotocol_hpsocket
{
    partial class TCPServerOld
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            label3 = new Label();
            txtPort = new TextBox();
            panel1 = new Panel();
            btnOpenCloseTCP = new Button();
            btnSendMsg = new Button();
            txtMsg = new TextBox();
            txtIPAddress = new TextBox();
            label4 = new Label();
            rTxtInfo = new RichTextBox();
            panel1.SuspendLayout();
            SuspendLayout();
            // 
            // label3
            // 
            label3.AutoSize = true;
            label3.Location = new Point(10, 48);
            label3.Margin = new Padding(2, 0, 2, 0);
            label3.Name = "label3";
            label3.Size = new Size(44, 17);
            label3.TabIndex = 33;
            label3.Text = "消息:";
            // 
            // txtPort
            // 
            txtPort.Location = new Point(228, 11);
            txtPort.Name = "txtPort";
            txtPort.Size = new Size(77, 23);
            txtPort.TabIndex = 32;
            txtPort.Text = "8085";
            // 
            // panel1
            // 
            panel1.Controls.Add(btnOpenCloseTCP);
            panel1.Location = new Point(449, 11);
            panel1.Margin = new Padding(2, 3, 2, 3);
            panel1.Name = "panel1";
            panel1.Size = new Size(166, 57);
            panel1.TabIndex = 30;
            // 
            // btnOpenCloseTCP
            // 
            btnOpenCloseTCP.Location = new Point(16, 9);
            btnOpenCloseTCP.Margin = new Padding(2, 3, 2, 3);
            btnOpenCloseTCP.Name = "btnOpenCloseTCP";
            btnOpenCloseTCP.Size = new Size(134, 38);
            btnOpenCloseTCP.TabIndex = 10;
            btnOpenCloseTCP.Text = "开启监听";
            btnOpenCloseTCP.UseVisualStyleBackColor = true;
            btnOpenCloseTCP.Click += btnOpenCloseTCP_Click;
            // 
            // btnSendMsg
            // 
            btnSendMsg.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Bold, GraphicsUnit.Point);
            btnSendMsg.Location = new Point(330, 20);
            btnSendMsg.Margin = new Padding(2, 3, 2, 3);
            btnSendMsg.Name = "btnSendMsg";
            btnSendMsg.Size = new Size(73, 38);
            btnSendMsg.TabIndex = 29;
            btnSendMsg.Text = "广播";
            btnSendMsg.UseVisualStyleBackColor = true;
            btnSendMsg.Click += btnSendMsg_Click;
            // 
            // txtMsg
            // 
            txtMsg.Location = new Point(70, 45);
            txtMsg.Margin = new Padding(2, 3, 2, 3);
            txtMsg.Name = "txtMsg";
            txtMsg.Size = new Size(235, 23);
            txtMsg.TabIndex = 28;
            // 
            // txtIPAddress
            // 
            txtIPAddress.Location = new Point(70, 11);
            txtIPAddress.Margin = new Padding(2, 3, 2, 3);
            txtIPAddress.Name = "txtIPAddress";
            txtIPAddress.Size = new Size(153, 23);
            txtIPAddress.TabIndex = 27;
            txtIPAddress.Text = "127.0.0.1";
            // 
            // label4
            // 
            label4.AutoSize = true;
            label4.Location = new Point(10, 14);
            label4.Margin = new Padding(2, 0, 2, 0);
            label4.Name = "label4";
            label4.Size = new Size(54, 17);
            label4.TabIndex = 26;
            label4.Text = "TCP地址";
            // 
            // rTxtInfo
            // 
            rTxtInfo.Location = new Point(10, 77);
            rTxtInfo.Name = "rTxtInfo";
            rTxtInfo.Size = new Size(605, 293);
            rTxtInfo.TabIndex = 34;
            rTxtInfo.Text = "";
            // 
            // TCPServerOld
            // 
            AutoScaleDimensions = new SizeF(7F, 17F);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(622, 382);
            Controls.Add(rTxtInfo);
            Controls.Add(label3);
            Controls.Add(txtPort);
            Controls.Add(panel1);
            Controls.Add(btnSendMsg);
            Controls.Add(txtMsg);
            Controls.Add(txtIPAddress);
            Controls.Add(label4);
            MaximizeBox = false;
            Name = "TCPServerOld";
            Text = "TCP服务端_Old";
            Load += TCPServerOld_Load;
            panel1.ResumeLayout(false);
            ResumeLayout(false);
            PerformLayout();
        }

        #endregion

        private Label label3;
        private TextBox txtPort;
        private Panel panel1;
        private Button btnOpenCloseTCP;
        private Button btnSendMsg;
        private TextBox txtMsg;
        private TextBox txtIPAddress;
        private Label label4;
        private RichTextBox rTxtInfo;
    }
}

 TCPServerOld.cs

using HPSocket;
using HPSocket.Base;
using HPSocket.Tcp;
using Newtonsoft.Json;
using System;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace csharp_networkprotocol_hpsocket
{
    public partial class TCPServerOld : Form
    {
        /// <summary>
        /// HPSocket_TCPServer示例帮助类(示例学习)
        /// </summary>
        public HPSocket_TCPServerHelper _HPSocket_TCPServerHelper = new HPSocket_TCPServerHelper();

        /// <summary>
        /// 包数据结束符
        /// </summary>
        private readonly string _endsWith = "\r\n";

        public TCPServerOld()
        {
            InitializeComponent();
        }
        private void TCPServerOld_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// 打开或关闭TCP
        /// </summary>
        private async void btnOpenCloseTCP_Click(object sender, EventArgs e)
        {
            try
            {
                if (btnOpenCloseTCP.Text == "开启监听")  // 开启监听
                {
                    // 检查地址
                    if (!CheckServerUrl())
                    {
                        return;
                    }

                    string localAddr = txtIPAddress.Text.Trim();  //IPAddress localAddr = IPAddress.Parse(txtIPAddress.Text.Trim());  // IP
                    int port = int.Parse(txtPort.Text);  // 端口

                    if (_HPSocket_TCPServerHelper._server == null || _HPSocket_TCPServerHelper.State != ServiceState.Started)
                    {
                        // <1>创建TCPServer对象
                        _HPSocket_TCPServerHelper.CreateTcpServer();  // 创建TcpServer
                        // <2>配置TCPServer的参数
                        _HPSocket_TCPServerHelper.Address = localAddr;      // IP
                        _HPSocket_TCPServerHelper.Port = (ushort)port;      // 端口
                        _HPSocket_TCPServerHelper.SocketBufferSize = 4096;  // 缓存4K
                        // <3>配置TCPServer的回调事件
                        _HPSocket_TCPServerHelper._server.OnAccept += OnAccept;                // TCP连接准备事件-使用附加数据处理黏包
                        _HPSocket_TCPServerHelper._server.OnHandShake += OnHandShake;          // TCP握手成功事件
                        _HPSocket_TCPServerHelper._server.OnPrepareListen += OnPrepareListen;  // 监听事件
                        _HPSocket_TCPServerHelper._server.OnSend += OnSend;                    // 数据包发送事件
                        _HPSocket_TCPServerHelper._server.OnReceive += OnReceive;              // 数据包接收事件
                        _HPSocket_TCPServerHelper._server.OnClose += OnClose;                  // TCP连接关闭事件
                        _HPSocket_TCPServerHelper._server.OnShutdown += OnShutdown;            // TCP服务器关闭事件
                    }
                    // <4>开启TCPServer服务
                    if (_HPSocket_TCPServerHelper.Start())
                    {
                        ShowBtnOpenCloseTCP(true, "关闭监听");
                    }
                    else
                    {
                        MessageBox.Show("开启监听失败!", "提示");
                    }
                }
                else  // 关闭监听
                {
                    _HPSocket_TCPServerHelper.Stop();
                    _HPSocket_TCPServerHelper._server.OnAccept -= OnAccept;                // TCP连接准备事件-使用附加数据处理黏包
                    _HPSocket_TCPServerHelper._server.OnHandShake -= OnHandShake;          // TCP握手成功事件
                    _HPSocket_TCPServerHelper._server.OnPrepareListen -= OnPrepareListen;  // 监听事件
                    _HPSocket_TCPServerHelper._server.OnSend -= OnSend;                    // 数据包发送事件
                    _HPSocket_TCPServerHelper._server.OnReceive -= OnReceive;              // 数据包接收事件
                    _HPSocket_TCPServerHelper._server.OnClose -= OnClose;                  // TCP连接关闭事件
                    _HPSocket_TCPServerHelper._server.OnShutdown -= OnShutdown;            // TCP服务器关闭事件

                    ShowBtnOpenCloseTCP(false, "开启监听");
                }
            }
            catch (Exception ex)
            {
                ShowBtnOpenCloseTCP(false, "开启监听");
                ShowLog($"开启/关闭监听失败,错误信息: {ex.Message}");
            }
        }

        /// <summary>
        /// 广播按钮
        /// </summary>
        private async void btnSendMsg_Click(object sender, EventArgs e)
        {
            try
            {
                // 检查地址
                if (!CheckServerUrl())
                {
                    return;
                }

                string hostname = txtIPAddress.Text;
                int port = int.Parse(txtPort.Text);
                string sendMsg = txtMsg.Text;  // 
                if (string.IsNullOrEmpty(sendMsg))
                {
                    ShowLog("发送信息不可为空!");
                    return;
                }

                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg + _endsWith);
                int length = bytes.Length;

                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendLine(string.Concat("TCPServer服务器广播了内容:“", sendMsg, "”"));

                List<IntPtr> connIds = _HPSocket_TCPServerHelper.GetAllConnectionIds();
                stringBuilder.AppendLine($"  客户端个数为{connIds.Count};客户端接收结果如下:");
                foreach (IntPtr connId in connIds)
                {
                    try
                    {
                        if (!_HPSocket_TCPServerHelper.GetLocalAddress(connId, out string ip, out ushort port1))
                        {
                            stringBuilder.AppendLine(string.Concat($"    客户端[{connId}]", ",接收结果:失败!连接已断开!"));
                            continue;
                        }
                        bool resultData_TCP = _HPSocket_TCPServerHelper.Send(connId, bytes, length);  // 连接并发送
                        //stringBuilder.AppendLine(string.Concat($"    客户端[{connId}]{ip}:{port1}", ",接收结果:", resultData_TCP, "_", $"[{_HPSocket_TCPServerHelper._server.ErrorCode}]", _HPSocket_TCPServerHelper._server.ErrorMessage));
                    }
                    catch (Exception ex)
                    {
                        stringBuilder.AppendLine(string.Concat($"    客户端[{connId}]", ",接收结果:失败! 错误信息:", ex.Message));
                    }
                }
                ShowLog(stringBuilder.ToString());
            }
            catch (Exception ex)
            {
                ShowLog("TCPServer服务器广播内容中止!错误信息:" + ex.Message);
            }
        }

        #region TCP事件
        /// <summary>
        /// TCP连接事件(连接前)-使用附加数据处理黏包,不可异步
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="client">如果为 TCP 连接,pClient为 SOCKET 句柄;如果为 UDP 连接,pClient为 SOCKADDR 指针;</param>
        /// <returns></returns>
        private HandleResult OnAccept(IServer sender, IntPtr connId, IntPtr client)
        {
            // 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }

            // 设置附加数据(用来做粘包处理)
            sender.SetExtra(connId, string.Empty);  // 初始化附加信息

            ShowLog(string.Format("TCP客户端接入({0}), ip: {1}, port: {2}", connId, ip, port));
            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IServer sender, IntPtr connId)
        {
            // 一般用不到
            return HandleResult.Ok;
        }

        /// <summary>
        /// 监听事件
        /// </summary>
        /// <param name="server">服务器对象</param>
        /// <param name="intPtr">连接ID</param>
        /// <returns></returns>
        private HandleResult OnPrepareListen(IServer server, IntPtr intPtr)
        {
            ShowLog(string.Format("TCP服务器开启监听({0}:{1}), listen:{2}", server.Address, server.Port, intPtr));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnSend(IServer sender, IntPtr connId, byte[] data)
        {
            sender.GetRemoteAddress(connId, out string ip, out ushort port);  // 获取客户端地址

            ShowLog(string.Format("TCP服务器发送数据[ID:{0},客户端地址:‘{1}:{2}’];数据[长度{3}]:{4}", connId, ip ?? "未找到IP", port, data.Length, Encoding.UTF8.GetString(data)));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包接收事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnReceive(IServer sender, IntPtr connId, byte[] data)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }
            // <2> 获取附加数据对象
            var extraDataStr = sender.GetExtra<string>(connId);
            if (extraDataStr == null)
            {
                return HandleResult.Error;
            }
            // <3> 将接收数据转换成字符串
            string msg = Encoding.UTF8.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)

            // <4> 显示信息
            ShowLog(string.Format("TCP服务器接收客户端[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", connId, ip, port, data.Length, msg));

            // <5> 处理数据
            HandleResult result;
            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整,忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <8> 保存PacketData数据
            if (extraDataStr.Length > _HPSocket_TCPServerHelper.SocketBufferSize)  // 可选-控制长度为4096(注:实际所占空间大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_HPSocket_TCPServerHelper.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.SetExtra(connId, extraDataStr);  // 初始化附加信息

            return result;
        }

        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="socketOperation">关闭的类型</param>
        /// <param name="errorCode">错误时的错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Ignore;
            }
            // <2> 释放附加数据
            if (sender.GetExtra<string>(connId) != null)
            {
                sender.RemoveExtra(connId);  // 删除附加数据
            }
            // <3> 显示信息
            ShowLog(string.Format("TCP客户端连接断开(ID {0},地址 {1}:{2}), 断开类型: {3},错误代码: {4}", connId, ip, port, socketOperation.ToString(), errorCode));

            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP服务器关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <returns></returns>
        private HandleResult OnShutdown(IServer sender)
        {
            ShowLog(string.Format("TCP服务器关闭({0}:{1})", sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        #endregion TCP事件

        #region 方法
        /// <summary>
        /// 检查连接是否正常
        /// </summary>
        /// <returns>true为通过</returns>
        private bool CheckServerUrl()
        {
            if (string.IsNullOrEmpty(txtIPAddress.Text))  // 
            {
                ShowLog("发送信息不可为空!");
                return false;
            }
            if (string.IsNullOrEmpty(txtPort.Text))  // 
            {
                ShowLog("端口不可为空!");
                return false;
            }
            return true;
        }
        #endregion 方法

        #region 操控页面控件
        /// <summary>
        /// 开关按钮刷新
        /// </summary>
        /// <param name="msg">信息</param>
        public void ShowBtnOpenCloseTCP(bool isSuccess, string text)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<bool, string>(ShowBtnOpenCloseTCP), isSuccess, text);
                return;
            }

            btnOpenCloseTCP.BackColor = isSuccess ? Color.FromArgb(128, 255, 128) : Color.FromArgb(255, 128, 128);
            btnOpenCloseTCP.Text = text;  // isSuccess? "关闭监听-控制线程" : "开启监听-控制线程";
        }

        /// <summary>
        /// 显示信息
        /// </summary>
        /// <param name="msg">信息</param>
        public void ShowLog(string msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<string>(ShowLog), msg);
                return;
            }

            if (rTxtInfo.Text.Length > 2000)
            {
                rTxtInfo.Text = string.Empty;
            }
            rTxtInfo.AppendText(string.Concat("\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), "->", msg));
        }
        #endregion 操控页面控件
    }
}

(2)TcpClient使用示例

易语言hpsocket教程_net socket框架

 TCPClientOld.Designer.cs

查看代码
 namespace HPSocket_Client
{
    partial class TCPClientOld
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            button1 = new Button();
            rtxtInfo = new RichTextBox();
            label2 = new Label();
            txtPort = new TextBox();
            btnSendMsg = new Button();
            txtMsg = new TextBox();
            txtIPAddress = new TextBox();
            label1 = new Label();
            txtPort2 = new TextBox();
            txtIP2 = new TextBox();
            checkBox1 = new CheckBox();
            SuspendLayout();
            // 
            // button1
            // 
            button1.Location = new Point(317, 38);
            button1.Name = "button1";
            button1.Size = new Size(57, 23);
            button1.TabIndex = 43;
            button1.Text = "连接";
            button1.UseVisualStyleBackColor = true;
            button1.Click += button1_Click;
            // 
            // rtxtInfo
            // 
            rtxtInfo.Location = new Point(12, 97);
            rtxtInfo.Name = "rtxtInfo";
            rtxtInfo.Size = new Size(598, 278);
            rtxtInfo.TabIndex = 42;
            rtxtInfo.Text = "";
            // 
            // label2
            // 
            label2.AutoSize = true;
            label2.Location = new Point(12, 72);
            label2.Margin = new Padding(2, 0, 2, 0);
            label2.Name = "label2";
            label2.Size = new Size(44, 17);
            label2.TabIndex = 41;
            label2.Text = "消息:";
            // 
            // txtPort
            // 
            txtPort.Location = new Point(251, 38);
            txtPort.Name = "txtPort";
            txtPort.Size = new Size(60, 23);
            txtPort.TabIndex = 40;
            txtPort.Text = "8085";
            // 
            // btnSendMsg
            // 
            btnSendMsg.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Bold, GraphicsUnit.Point);
            btnSendMsg.Location = new Point(400, 12);
            btnSendMsg.Margin = new Padding(2, 3, 2, 3);
            btnSendMsg.Name = "btnSendMsg";
            btnSendMsg.Size = new Size(209, 46);
            btnSendMsg.TabIndex = 39;
            btnSendMsg.Text = "发送";
            btnSendMsg.UseVisualStyleBackColor = true;
            btnSendMsg.Click += btnSendMsg_Click;
            // 
            // txtMsg
            // 
            txtMsg.Location = new Point(67, 69);
            txtMsg.Margin = new Padding(2, 3, 2, 3);
            txtMsg.Name = "txtMsg";
            txtMsg.Size = new Size(542, 23);
            txtMsg.TabIndex = 38;
            // 
            // txtIPAddress
            // 
            txtIPAddress.Location = new Point(82, 38);
            txtIPAddress.Margin = new Padding(2, 3, 2, 3);
            txtIPAddress.Name = "txtIPAddress";
            txtIPAddress.Size = new Size(164, 23);
            txtIPAddress.TabIndex = 37;
            txtIPAddress.Text = "127.0.0.1";
            // 
            // label1
            // 
            label1.AutoSize = true;
            label1.Location = new Point(12, 41);
            label1.Margin = new Padding(2, 0, 2, 0);
            label1.Name = "label1";
            label1.Size = new Size(66, 17);
            label1.TabIndex = 36;
            label1.Text = "TCP服务器";
            // 
            // txtPort2
            // 
            txtPort2.Location = new Point(323, 6);
            txtPort2.Name = "txtPort2";
            txtPort2.Size = new Size(60, 23);
            txtPort2.TabIndex = 46;
            txtPort2.Text = "8086";
            // 
            // txtIP2
            // 
            txtIP2.Location = new Point(154, 6);
            txtIP2.Margin = new Padding(2, 3, 2, 3);
            txtIP2.Name = "txtIP2";
            txtIP2.Size = new Size(164, 23);
            txtIP2.TabIndex = 45;
            txtIP2.Text = "127.0.0.1";
            // 
            // checkBox1
            // 
            checkBox1.AutoSize = true;
            checkBox1.Location = new Point(16, 8);
            checkBox1.Name = "checkBox1";
            checkBox1.Size = new Size(133, 21);
            checkBox1.TabIndex = 48;
            checkBox1.Text = "绑定TCP客户端地址";
            checkBox1.UseVisualStyleBackColor = true;
            // 
            // TCPClientOld
            // 
            AutoScaleDimensions = new SizeF(7F, 17F);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(620, 387);
            Controls.Add(checkBox1);
            Controls.Add(txtPort2);
            Controls.Add(txtIP2);
            Controls.Add(button1);
            Controls.Add(rtxtInfo);
            Controls.Add(label2);
            Controls.Add(txtPort);
            Controls.Add(btnSendMsg);
            Controls.Add(txtMsg);
            Controls.Add(txtIPAddress);
            Controls.Add(label1);
            Name = "TCPClientOld";
            Text = "TCPClientOld";
            ResumeLayout(false);
            PerformLayout();
        }

        #endregion

        private Button button1;
        private RichTextBox rtxtInfo;
        private Label label2;
        private TextBox txtPort;
        private Button btnSendMsg;
        private TextBox txtMsg;
        private TextBox txtIPAddress;
        private Label label1;
        private TextBox txtPort2;
        private TextBox txtIP2;
        private CheckBox checkBox1;
    }
}

 TCPClientOld.cs

using csharp_networkprotocol_hpsocket;
using HPSocket;
using HPSocket.Base;
using HPSocket.Sdk;
using System;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;

namespace HPSocket_Client
{
    public partial class TCPClientOld : Form
    {
        /// <summary>
        ///  HPSocket_TCPServer示例帮助类(示例学习)
        /// </summary>
        HPSocket_TcpClientHelper _HPSocket_TcpClientHelper = new HPSocket_TcpClientHelper();

        /// <summary>
        /// 包数据结束符
        /// </summary>
        private readonly string _endsWith = "\r\n";

        public TCPClientOld()
        {
            InitializeComponent();
        }

        private void TCPClientOld_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// 连接
        /// </summary>
        private async void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (button1.Text == "连接")  // 开启监听
                {
                    // 检查地址
                    if (!CheckServerUrl())
                    {
                        return;
                    }

                    string address = txtIPAddress.Text.Trim();        // IP
                    ushort port = ushort.Parse(txtPort.Text.Trim());  // 端口

                    // <2>配置TCPServer的参数
                    _HPSocket_TcpClientHelper.Address = address;        // IP
                    _HPSocket_TcpClientHelper.Port = port;              // 端口
                    _HPSocket_TcpClientHelper.SocketBufferSize = 4096;  // 缓存4K
                    if (checkBox1.Checked)
                    {
                        _HPSocket_TcpClientHelper.BindAddress = txtIP2.Text.Trim();  // 本地绑定到哪个ip
                        _HPSocket_TcpClientHelper.BindPort = (ushort)Convert.ToInt32(txtPort2.Text.Trim());   // 本地绑定到哪个端口
                    }

                    // <3> 绑定事件
                    //event ClientPrepareConnectEventHandler OnPrepareConnect;  // 准备连接了事件
                    //event ClientConnectEventHandler OnConnect;      // 连接事件
                    //event ClientHandShakeEventHandler OnHandShake;  // TCP握手成功事件
                    //event ClientSendEventHandler OnSend;            // 数据包发送事件
                    //event ClientReceiveEventHandler OnReceive;      // 数据包到达事件
                    //event ClientCloseEventHandler OnClose;          // TCP连接关闭事件
                    _HPSocket_TcpClientHelper._client.OnPrepareConnect += OnPrepareConnect;  // 准备连接了事件
                    _HPSocket_TcpClientHelper._client.OnConnect += OnConnect;                // 连接事件
                    _HPSocket_TcpClientHelper._client.OnHandShake += OnHandShake;            // TCP握手成功事件
                    _HPSocket_TcpClientHelper._client.OnSend += OnSend;                      // 数据包发送事件
                    _HPSocket_TcpClientHelper._client.OnReceive += OnReceive;                // 数据包到达事件
                    _HPSocket_TcpClientHelper._client.OnClose += OnClose;                    // TCP连接关闭事件

                    bool connResult = _HPSocket_TcpClientHelper.Connect();  // 连接
                    if (connResult)
                    {
                        Showbutton1(true, "断开");
                    }
                    else
                    {
                        Showbutton1(false, "连接");
                    }
                }
                else  // 关闭监听
                {
                    _HPSocket_TcpClientHelper.Stop();
                    _HPSocket_TcpClientHelper._client.OnPrepareConnect -= OnPrepareConnect;  // 准备连接了事件
                    _HPSocket_TcpClientHelper._client.OnConnect -= OnConnect;                // 连接事件
                    _HPSocket_TcpClientHelper._client.OnHandShake -= OnHandShake;            // TCP握手成功事件
                    _HPSocket_TcpClientHelper._client.OnSend -= OnSend;                      // 数据包发送事件
                    _HPSocket_TcpClientHelper._client.OnReceive -= OnReceive;                // 数据包到达事件
                    _HPSocket_TcpClientHelper._client.OnClose -= OnClose;                    // TCP连接关闭事件

                    Showbutton1(false, "连接");
                }
            }
            catch (Exception ex)
            {
                ShowLog($"连接/关闭TCP客户端 {txtIPAddress.Text}:{txtPort.Text} 失败,错误信息:" + ex.Message);
            }
        }

        /// <summary>
        /// 发送
        /// </summary>
        private async void btnSendMsg_Click(object sender, EventArgs e)
        {
            try
            {
                // 检查地址
                if (!_HPSocket_TcpClientHelper._client.IsConnected || _HPSocket_TcpClientHelper._client.State != ServiceState.Started)
                {
                    ShowLog("TCP客户端未连接到任何TCP服务器!");
                    return;
                }
                string sendMsg = txtMsg.Text;  // 
                if (string.IsNullOrEmpty(sendMsg))
                {
                    ShowLog("发送信息不可为空!");
                    return;
                }
                byte[] bytes = Encoding.UTF8.GetBytes(sendMsg);
                int length = bytes.Length;

                bool resultData_TCP = _HPSocket_TcpClientHelper.Send(bytes, length);  // 发送
                // 显示信息-略
            }
            catch (Exception ex)
            {
                ShowLog("发送信息错误,错误信息:" + ex.Message);
            }
        }

        #region 事件
        /// <summary>
        /// 准备连接了事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socket">客户端Id</param>
        /// <returns></returns>
        private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
        {
            sender.ExtraData = string.Empty;  // 设置附加数据(用来做粘包处理)

            return HandleResult.Ok;
        }
        /// <summary>
        /// 连接事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnConnect(IClient sender)
        {
            ShowLog(string.Format("TCP客户端([{0}]{1}:{2})接入TCP服务器{3}:{4}", sender.ConnectionId, sender.BindAddress, sender.BindPort, sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IClient sender)
        {
            // 一般用不到
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnSend(IClient sender, byte[] data)
        {
            ShowLog(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]:{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包到达事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnReceive(IClient sender, byte[] data)
        {
            // <1> 获取附加数据对象
            if (!(sender.ExtraData is string extraDataStr))
            {
                return HandleResult.Error;
            }

            // <2> 将接收数据转换成字符串
            string msg = Encoding.UTF8.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)

            // <3> 显示信息
            ShowLog(string.Format("TCP客户端接收到TCP服务器[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, msg));

            // <4> 处理数据
            HandleResult result;
            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整,忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <5> 保存PacketData数据
            if (extraDataStr.Length > _HPSocket_TcpClientHelper.SocketBufferSize)  // 可选-控制长度为4096(注:实际所占空间大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_HPSocket_TcpClientHelper.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.ExtraData = extraDataStr;  // 初始化附加信息

            return result;
        }
        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socketOperation">关闭类型</param>
        /// <param name="errorCode">错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
        {
            sender.ExtraData = null;  // 删除附加数据
            ShowLog(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
            return HandleResult.Ok;
        }
        #endregion 事件

        #region 方法
        /// <summary>
        /// 开关按钮刷新
        /// </summary>
        /// <param name="msg">信息</param>
        public void Showbutton1(bool isSuccess, string text)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<bool, string>(Showbutton1), isSuccess, text);
                return;
            }

            button1.BackColor = isSuccess ? Color.FromArgb(128, 255, 128) : Color.FromArgb(255, 128, 128);
            button1.Text = text;  // isSuccess? "关闭监听-控制线程" : "开启监听-控制线程";
        }

        /// <summary>
        /// 显示信息
        /// </summary>
        /// <param name="msg">信息</param>
        public void ShowLog(string msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<string>(ShowLog), msg);
                return;
            }
            if (rtxtInfo.Text.Length > 2000)
            {
                rtxtInfo.Text = string.Empty;
            }
            rtxtInfo.AppendText(string.Concat("\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), "->", msg));
        }

        /// <summary>
        /// 检查连接是否正常
        /// </summary>
        /// <returns>true为通过</returns>
        private bool CheckServerUrl()
        {

            if (string.IsNullOrEmpty(txtIPAddress.Text))  // 
            {
                ShowLog("发送信息不可为空!");
                return false;
            }
            if (string.IsNullOrEmpty(txtPort.Text))  // 
            {
                ShowLog("端口不可为空!");
                return false;
            }
            return true;
        }
        #endregion 方法
    }
}

(3)HPSocket的TCPServer、 TCPServer类学习Helper(类库提供的方法、属性学习)

查看代码
 /**
*┌──────────────────────────────────────────────────────────────┐
*│ 描    述:HPSocket的TCPServer、TCPClient通讯相关的工具类(类库提供的方法、属性学习)
*│ 作    者:执笔小白
*│ 版    本:1.0
*│ 创建时间:2023-6-13 10:40:56
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: ZhibiXiaobai
*│ 类    名:HPSocket_TCPServerHelper、HPSocket_TcpClientHelper
*└──────────────────────────────────────────────────────────────┘
*/
using HPSocket;
using HPSocket.Base;
using HPSocket.Sdk;
using HPSocket.Tcp;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace csharp_networkprotocol_hpsocket
{
    /// <summary>
    /// HPSocket_TCPServer示例帮助类(类库提供的方法、属性学习)
    /// System.Net.Sockets类库
    /// TcpServer
    /// </summary>
    public class HPSocket_TCPServerHelper
    {
        #region 接收端(服务端)
        /// <summary>
        /// 侦听来自 TCP 网络客户端的连接
        /// </summary>
        public ITcpServer _server = null;
        #region 创建时的设置项
        /// <summary>
        /// 要绑定的服务器地址
        /// </summary>
        public string Address
        {
            get => _server.Address;
            set => _server.Address = value;
        }

        /// <summary>
        /// 要绑定的服务器端口
        /// </summary>
        public ushort Port
        {
            get => _server.Port;
            set => _server.Port = value;
        }

        /// <summary>
        /// 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)
        /// </summary>
        public uint MaxConnectionCount
        {
            get => _server.MaxConnectionCount;
            set => _server.MaxConnectionCount = value;
        }

        /// <summary>
        /// 读取或设置是否标记静默时间
        /// (设置为 true 时 DisconnectSilenceConnections() 和 GetSilencePeriod()才有效,默认:false)
        /// </summary>   
        public bool IsMarkSilence
        {
            get => _server.IsMarkSilence;
            set => _server.IsMarkSilence = value;
        }

        /// <summary>
        /// 获取或设置数据发送策略
        /// </summary>
        public SendPolicy SendPolicy
        {
            get => _server.SendPolicy;
            set => _server.SendPolicy = value;
        }

        /// <summary>
        /// 获取或设置 OnSend 事件同步策略
        /// </summary>
        public OnSendSyncPolicy OnSendSyncPolicy
        {
            get => _server.OnSendSyncPolicy;
            set => _server.OnSendSyncPolicy = value;
        }

        /// <summary>
        /// 获取或设置地址重用选项
        /// </summary>
        public ReuseAddressPolicy ReuseAddressPolicy
        {
            get => _server.ReuseAddressPolicy;
            set => _server.ReuseAddressPolicy = value;
        }

        /// <summary>
        /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
        /// </summary>
        public bool NoDelay
        {
            get => _server.NoDelay;
            set => _server.NoDelay = value;
        }

        #region 心跳检测
        /// <summary>
        /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
        /// </summary>
        public uint KeepAliveTime
        {
            get => _server.KeepAliveTime;
            set => _server.KeepAliveTime = value;
        }


        /// <summary>
        /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
        /// </summary>
        public uint KeepAliveInterval
        {
            get => _server.KeepAliveInterval;
            set => _server.KeepAliveInterval = value;
        }

        #endregion 心跳检测

        #region 性能优化
        /// <summary>
        /// 读取或设置 Accept 预投递数量(根据负载调整设置,Accept 预投递数量越大则支持的并发连接请求越多)
        /// </summary>
        public uint AcceptSocketCount
        {
            get => _server.AcceptSocketCount;
            set => _server.AcceptSocketCount = value;
        }


        /// <summary>
        /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数)
        /// </summary>
        public uint SocketBufferSize
        {
            get => _server.SocketBufferSize;
            set => _server.SocketBufferSize = value;
        }


        /// <summary>
        /// 读取或设置监听 Socket 的等候队列大小(根据并发连接数量调整设置)
        /// </summary>
        public uint SocketListenQueue
        {
            get => _server.SocketListenQueue;
            set => _server.SocketListenQueue = value;
        }


        /// <summary>
        /// 读取或设置工作线程数量(通常设置为 2 * CPU + 2)
        /// </summary>
        public uint WorkerThreadCount
        {
            get => _server.WorkerThreadCount;
            set => _server.WorkerThreadCount = value;
        }


        /// <summary>
        /// 读取或设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用)
        /// </summary>
        public uint FreeSocketObjLockTime
        {
            get => _server.FreeSocketObjLockTime;
            set => _server.FreeSocketObjLockTime = value;
        }


        /// <summary>
        /// 读取或设置 Socket 缓存池大小(通常设置为平均并发连接数量的 1/3 - 1/2)
        /// </summary>
        public uint FreeSocketObjPool
        {
            get => _server.FreeSocketObjPool;
            set => _server.FreeSocketObjPool = value;
        }


        /// <summary>
        /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
        /// </summary>
        public uint FreeBufferObjPool
        {
            get => _server.FreeBufferObjPool;
            set => _server.FreeBufferObjPool = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
        /// </summary>
        public uint FreeSocketObjHold
        {
            get => _server.FreeSocketObjHold;
            set => _server.FreeSocketObjHold = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
        /// </summary>
        public uint FreeBufferObjHold
        {
            get => _server.FreeBufferObjHold;
            set => _server.FreeBufferObjHold = value;
        }

        #endregion 性能优化
        #endregion 创建时的设置项

        #region 常用属性
        /// <summary>
        /// 获取是否启动
        /// </summary>
        public bool HasStarted => _server.HasStarted;

        /// <summary>
        /// 获取状态
        /// </summary>
        public ServiceState State => _server.State;

        /// <summary>
        /// 获取连接数
        /// </summary>
        public uint ConnectionCount => _server.ConnectionCount;

        /// <summary>
        /// 是否为安全连接(SSL/HTTPS)
        /// </summary>
        public bool IsSecure => _server.IsSecure;
        #endregion 常用属性

        #region 常用方法
        /// <summary>
        /// 启动服务
        /// </summary>
        /// <returns></returns>
        public bool Start() => _server.Start();

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <returns></returns>
        public bool Stop() => _server.Stop();

        #region 附加信息
        /// <summary>
        /// 获取所有附加数据
        /// </summary>
        /// <returns></returns>
        public ConcurrentDictionary<IntPtr, object> GetAllExtra() => _server.GetAllExtra();

        /// <summary>
        /// 获取附加数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="connId"></param>
        /// <returns></returns>
        public T GetExtra<T>(IntPtr connId) => _server.GetExtra<T>(connId);

        /// <summary>
        /// 获取连接附加数据, 非托管版本, hp-socket自带方法;非特殊需求不要使用这个方法, 请直接使用 GetExtra();
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="extra"></param>
        /// <returns></returns>
        public bool NativeGetConnectionExtra(IntPtr connId, out IntPtr extra) => _server.NativeGetConnectionExtra(connId, out extra);

        /// <summary>
        /// 设置附加数据
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool SetExtra(IntPtr connId, object obj) => _server.SetExtra(connId, obj);

        /// <summary>
        /// 设置连接附加数据, 非托管版本, hp-socket自带方法;非特殊需求不要使用这个方法, 请直接使用 SetExtra();
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="extra"></param>
        /// <returns></returns>
        public bool NativeSetConnectionExtra(IntPtr connId, IntPtr extra) => _server.NativeSetConnectionExtra(connId, extra);

        /// <summary>
        /// 删除附加数据
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool RemoveExtra(IntPtr connId) => _server.RemoveExtra(connId);
        #endregion 附加信息

        #region 发送数据
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="connId">连接id</param>
        /// <param name="bytes">数据源</param>
        /// <param name="length">数据长度</param>
        /// <returns></returns>
        public bool Send(IntPtr connId, byte[] bytes, int length) => _server.Send(connId, bytes, length);

        /// <summary>
        /// 发送数据-指针偏移
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="bytes"></param>
        /// <param name="offset">对bytes的偏移</param>
        /// <param name="length">发多大</param>
        /// <returns></returns>
        public bool Send(IntPtr connId, byte[] bytes, int offset, int length) => _server.Send(connId, bytes, offset, length);
        /// <summary>
        /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
        /// </summary>
        /// <param name="connId">连接 ID</param>
        /// <param name="buffers">发送缓冲区数组</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendPackets(IntPtr connId, Wsabuf[] buffers) => SendPackets(connId, buffers);
        #endregion 发送数据

        #region 发送本地小文件
        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据</param>
        /// <param name="tail">尾部附加数据</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(IntPtr connId, string filePath, ref Wsabuf head, ref Wsabuf tail) => _server.SendSmallFile(connId, filePath, ref head, ref tail);

        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据,可以为null</param>
        /// <param name="tail">尾部附加数据,可以为null</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(IntPtr connId, string filePath, byte[] head, byte[] tail) => _server.SendSmallFile(connId, filePath, head, tail);
        #endregion 发送本地小文件

        #region 查询服务器信息
        /// <summary>
        /// 获取监听socket的地址信息
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetListenAddress(out string ip, out ushort port) => _server.GetListenAddress(out ip, out port);
        #endregion 查询服务器信息

        #region 查询客户端信息
        /// <summary>
        /// 获取所有连接
        /// </summary>
        /// <returns></returns>
        public List<IntPtr> GetAllConnectionIds() => _server.GetAllConnectionIds();

        /// <summary>
        /// 获取某客户端连接是否有效
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool IsConnected(IntPtr connId) => _server.IsConnected(connId);

        /// <summary>
        /// 获取某客户端连接的接收状态
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public ReceiveState GetReceiveState(IntPtr connId) => _server.GetReceiveState(connId);

        /// <summary>
        /// 获取某个连接的本地地址信息
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetLocalAddress(IntPtr connId, out string ip, out ushort port) => _server.GetLocalAddress(connId, out ip, out port);

        /// <summary>
        /// 获取某个连接的远程地址信息
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetRemoteAddress(IntPtr connId, out string ip, out ushort port) => _server.GetRemoteAddress(connId, out ip, out port);

        /// <summary>
        /// 获取指定连接的连接时长(毫秒)
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="period"></param>
        /// <returns></returns>
        public bool GetConnectPeriod(IntPtr connId, out uint period) => _server.GetConnectPeriod(connId, out period);

        /// <summary>
        /// 获取某个连接静默时间(毫秒)
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="period"></param>
        /// <returns></returns>
        public bool GetSilencePeriod(IntPtr connId, out uint period) => _server.GetSilencePeriod(connId, out period);

        /// <summary>
        /// 获取连接中未发出数据的长度
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool GetPendingDataLength(IntPtr connId, out int length) => _server.GetPendingDataLength(connId, out length);

        #endregion 查询客户端信息

        #region 断开与客户端的连接
        /// <summary>
        /// 断开与某个客户的连接
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="force">是否强制断开</param>
        /// <returns></returns>
        public bool Disconnect(IntPtr connId, bool force = true) => _server.Disconnect(connId, force);

        /// <summary>
        /// 断开超过指定时间的连接
        /// </summary>
        /// <param name="period">毫秒</param>
        /// <param name="force">强制</param>
        /// <returns></returns>
        public bool DisconnectLongConnections(uint period, bool force = true) => _server.DisconnectLongConnections(period, force);

        /// <summary>
        /// 断开超过指定时长的静默连接
        /// </summary>
        /// <param name="period">毫秒</param>
        /// <param name="force">强制</param>
        /// <returns></returns>
        public bool DisconnectSilenceConnections(uint period, bool force = true) => _server.DisconnectSilenceConnections(period, force);
        #endregion 断开与客户端的连接

        #region 暂停/唤醒接收某客户端
        /// <summary>
        /// 暂停接收
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool PauseReceive(IntPtr connId) => _server.PauseReceive(connId);

        /// <summary>
        /// 唤醒接收
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool ResumeReceive(IntPtr connId) => _server.ResumeReceive(connId);
        #endregion 暂停/唤醒接收某客户端
        #endregion 常用方法

        #region 创建侦听的方法与示例方法(后使用Start()开启侦听)
        /// <summary>
        /// 创建侦听
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpServer CreateTcpServer()
        {
            _server = new TcpServer();
            return _server;
        }
        /// <summary>
        /// 创建TCP侦听 -示例
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpServer CreateTcpServerDemo(IPAddress localAddr, int port)
        {
            // <1> 创建服务器对象
            _server = new TcpServer();

            // <2> 设置socket接收长度
            _server.SocketBufferSize = 4096;  // 4K
            _server.Address = "192.168.10.11";
            _server.Port = 8085;

            // <3> 绑定事件
            //event ServerAcceptEventHandler OnAccept;                // TCP连接准备事件
            //event ServerHandShakeEventHandler OnHandShake;          // TCP握手成功事件
            //event ServerPrepareListenEventHandler OnPrepareListen;  // 监听事件
            //event ServerSendEventHandler OnSend;                    // 数据包发送事件
            //event ServerReceiveEventHandler OnReceive;              // 数据包到达事件
            //event ServerCloseEventHandler OnClose;                  // TCP连接关闭事件
            //event ServerShutdownEventHandler OnShutdown;            // TCP服务器关闭事件

            _server.OnAccept += OnAccept;                // TCP连接准备事件-使用附加数据处理黏包
            _server.OnHandShake += OnHandShake;          // TCP握手成功事件
            _server.OnPrepareListen += OnPrepareListen;  // 监听事件
            _server.OnSend += OnSend;                    // 数据包发送事件
            _server.OnReceive += OnReceive;              // 数据包接收事件
            _server.OnClose += OnClose;                  // TCP连接关闭事件
            _server.OnShutdown += OnShutdown;            // TCP服务器关闭事件
            return _server;
        }

        #region TCP事件
        /// <summary>
        /// TCP连接事件(连接前)-使用附加数据处理黏包,不可异步
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="client">如果为 TCP 连接,pClient为 SOCKET 句柄;如果为 UDP 连接,pClient为 SOCKADDR 指针;</param>
        /// <returns></returns>
        private HandleResult OnAccept(IServer sender, IntPtr connId, IntPtr client)
        {
            // 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }

            // 设置附加数据(用来做粘包处理)
            sender.SetExtra(connId, string.Empty);  // 初始化附加信息

            //ShowLog(string.Format("TCP客户端接入({0}), ip: {1}, port: {2}", connId, ip, port));
            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IServer sender, IntPtr connId)
        {
            // 一般用不到
            return HandleResult.Ok;
        }

        /// <summary>
        /// 监听事件
        /// </summary>
        /// <param name="server">服务器对象</param>
        /// <param name="intPtr">连接ID</param>
        /// <returns></returns>
        private HandleResult OnPrepareListen(IServer server, IntPtr intPtr)
        {
            //ShowLog(string.Format("TCP服务器开启监听({0}:{1}), listen:{2}", server.Address, server.Port, intPtr));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnSend(IServer sender, IntPtr connId, byte[] data)
        {
            sender.GetRemoteAddress(connId, out string ip, out ushort port);  // 获取客户端地址

            //ShowLog(string.Format("TCP服务器发送数据[ID:{0},客户端地址:‘{1}:{2}’];数据[长度{3}]:{4}", connId, ip ?? "未找到IP", port, data.Length, Encoding.ASCII.GetString(data)));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包接收事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnReceive(IServer sender, IntPtr connId, byte[] data)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }
            // <2> 获取附加数据对象
            var extraDataStr = sender.GetExtra<string>(connId);
            if (extraDataStr == null)
            {
                return HandleResult.Error;
            }
            // <3> 将接收数据转换成字符串
            string msg = Encoding.ASCII.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)

            // <4> 显示信息
            //ShowLog(string.Format("TCP服务器接收客户端[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", connId, ip, port, data.Length, msg));

            // <5> 处理数据
            HandleResult result;

            string _endsWith = "\r\n";  // TCP包的数据结束符

            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整,忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <8> 保存PacketData数据
            if (extraDataStr.Length > _server.SocketBufferSize)  // 可选-控制长度为4096(注:实际所占控件大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_server.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.SetExtra(connId, extraDataStr);  // 初始化附加信息

            return result;
        }

        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="socketOperation">关闭的类型</param>
        /// <param name="errorCode">错误时的错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Ignore;
            }
            // <2> 释放附加数据
            if (sender.GetExtra<string>(connId) != null)
            {
                sender.RemoveExtra(connId);  // 删除附加数据
            }
            // <3> 显示信息
            //ShowLog(string.Format("TCP客户端连接断开(ID {0},地址 {1}:{2}), 断开类型: {3},错误代码: {4}", connId, ip, port, socketOperation.ToString(), errorCode));

            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP服务器关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <returns></returns>
        private HandleResult OnShutdown(IServer sender)
        {
            //ShowLog(string.Format("TCP服务器关闭({0}:{1})", sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        #endregion TCP事件
        #endregion 创建侦听的方法与示例方法(后使用Start()开启侦听)
        #endregion 接收端(服务端)
    }

    /// <summary>
    /// HPSocket_TcpClient示例帮助类(示例学习)
    /// System.Net.Sockets类库
    /// TcpClient
    /// </summary>
    public class HPSocket_TcpClientHelper
    {
        #region 发送端(客户端)
        /// <summary>
        /// TCP客户端
        /// </summary>
        public ITcpClient _client = new TcpClient();

        /// <summary>
        /// 本地绑定到哪个ip
        /// </summary>
        public string BindAddress
        {
            get => _client.BindAddress;
            set => _client.BindAddress = value;
        }
        /// <summary>
        /// 本地绑定到哪个端口
        /// </summary>
        public ushort BindPort
        {
            get => _client.BindPort;
            set => _client.BindPort = value;
        }

        /// <summary>
        /// 远程服务器地址
        /// </summary>
        public string Address
        {
            get => _client.Address;
            set => _client.Address = value;
        }
        /// <summary>
        /// 远程服务器端口
        /// </summary>
        public ushort Port
        {
            get => _client.Port;
            set => _client.Port = value;
        }

        #region 创建时的设置项
        /// <summary>
        /// 是否异步连接,默认为真
        /// </summary>
        public bool Async
        {
            get => _client.Async;
            set => _client.Async = value;
        }

        /// <summary>
        /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为:(N * 1024) - sizeof(TBufferObj))
        /// </summary>
        public uint SocketBufferSize
        {
            get => _client.SocketBufferSize;
            set => _client.SocketBufferSize = value;
        }

        /// <summary>
        /// 连接超时时间, 默认操作系统默认值
        /// 单位: 毫秒
        /// 异常:T:System.InvalidOperationException:同步连接、.NET Framework2.0以及设置小于100毫秒会引发此异常
        /// </summary>
        public int ConnectionTimeout
        {
            get => _client.ConnectionTimeout;
            set => _client.ConnectionTimeout = value;
        }

        /// <summary>
        /// 附加数据
        /// 赋值:client.ExtraData = myObj;
        /// 取值:var data = ExtraData as MyData;
        /// </summary>
        public object ExtraData
        {
            get => _client.ExtraData;
            set => _client.ExtraData = value;
        }

        /// <summary>
        /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
        /// </summary>
        public bool NoDelay
        {
            get => _client.NoDelay;
            set => _client.NoDelay = value;
        }

        #region 心跳
        /// <summary>
        /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
        /// </summary>
        public uint KeepAliveTime
        {
            get => _client.KeepAliveTime;
            set => _client.KeepAliveTime = value;
        }
        /// <summary>
        /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
        /// </summary>
        public uint KeepAliveInterval
        {
            get => _client.KeepAliveInterval;
            set => _client.KeepAliveInterval = value;
        }
        #endregion 心跳

        /// <summary>
        /// 获取或设置暂停接收状态,设置状态时,不允许设置为ReceiveState.Unknown,
        /// </summary>
        public ReceiveState PauseReceive
        {
            get => _client.PauseReceive;
            set => _client.PauseReceive = value;
        }

        /// <summary>
        /// 获取或设置地址重用选项
        /// </summary>
        public ReuseAddressPolicy ReuseAddressPolicy
        {
            get => _client.ReuseAddressPolicy;
            set => _client.ReuseAddressPolicy = value;
        }

        /// <summary>
        /// 代理列表
        /// </summary>
        public List<IProxy> ProxyList
        {
            get => _client.ProxyList;
            set => _client.ProxyList = value;
        }

        #region 性能优化
        /// <summary>
        /// 读取或设置内存块缓存池大小(通常设置为 -> PUSH 模型:5 - 10;PULL 模型:10 - 20 )
        /// </summary>
        public uint FreeBufferPoolSize
        {
            get => _client.FreeBufferPoolSize;
            set => _client.FreeBufferPoolSize = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
        /// </summary>
        public uint FreeBufferPoolHold
        {
            get => _client.FreeBufferPoolHold;
            set => _client.FreeBufferPoolHold = value;
        }
        #endregion 性能优化
        #endregion 创建时的设置项

        #region 常用属性
        /// <summary>
        /// 检查通信组件是否已启动
        /// </summary>
        public bool HasStarted => _client.HasStarted;

        /// <summary>
        /// 状态
        /// </summary>
        public ServiceState State => _client.State;

        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected => _client.IsConnected;

        /// <summary>
        /// 是否为安全连接(SSL/HTTPS)
        /// </summary>
        public bool IsSecure => _client.IsSecure;

        /// <summary>
        /// 获取该组件对象的连接Id
        /// </summary>
        public IntPtr ConnectionId => _client.ConnectionId;
        #endregion 常用属性

        #region 常用方法
        #region 启动并连接到服务器
        /// <summary>
        /// 启动通讯组件并连接到服务器
        /// </summary>
        /// <returns></returns>
        public bool Connect() => _client.Connect();

        /// <summary>
        /// 启动通讯组件并连接到服务器
        /// </summary>
        /// <param name="address">远程服务器地址</param>
        /// <param name="port">远程服务器端口</param>
        /// <returns></returns>
        public bool Connect(string address, ushort port) => _client.Connect(address, port);
        #endregion 启动并连接到服务器

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <returns></returns>
        public bool Stop() => _client.Stop();

        #region 发送数据
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据包</param>
        /// <param name="length">数据包长度</param>
        /// <returns></returns>
        public bool Send(byte[] bytes, int length) => _client.Send(bytes, length);

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据包</param>
        /// <param name="offset">针对bytes的偏移</param>
        /// <param name="length">数据包长度</param>
        /// <returns></returns>
        public bool Send(byte[] bytes, int offset, int length) => _client.Send(bytes, offset, length);

        /// <summary>
        /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
        /// </summary>
        /// <param name="buffers">发送缓冲区数组</param>
        /// <param name="count">发送缓冲区数目</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendPackets(Wsabuf[] buffers, int count) => _client.SendPackets(buffers, count);
        #endregion 发送数据

        #region 发送本地小文件
        /// <summary>
        /// 发送本地小文件
        //  向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据</param>
        /// <param name="tail">尾部附加数据</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(string filePath, ref Wsabuf head, ref Wsabuf tail) => _client.SendSmallFile(filePath, ref head, ref tail);

        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据,可以为null</param>
        /// <param name="tail">尾部附加数据,可以为null</param>
        /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(string filePath, byte[] head, byte[] tail) => _client.SendSmallFile(filePath, head, tail);
        #endregion 发送本地小文件

        #region 查询连接信息
        /// <summary>
        /// 获取监听socket的地址信息
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetListenAddress(out string host, out ushort port) => _client.GetListenAddress(out host, out port);

        /// <summary>
        /// 获取连接的远程主机信息
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetRemoteHost(out string host, out ushort port) => _client.GetRemoteHost(out host, out port);

        /// <summary>
        /// 获取连接中未发出数据的长度
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool GetPendingDataLength(out int length) => _client.GetPendingDataLength(out length);
        #endregion 查询连接信息

        /// <summary>
        /// 等待代理结果
        /// </summary>
        /// <returns>连接成功返回true, 连接失败返回false</returns>
        public async Task<bool> WaitProxyAsync() => await _client.WaitProxyAsync();
        #endregion 常用方法

        #region 创建TCPClient并连接示例    
        /// <summary>
        /// 创建TCP侦听 -示例
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpClient CreateTcpServerDemo(string localAddr, int port)
        {
            // <2>配置TCPServer的参数
            _client.Address = localAddr;      // IP
            _client.Port = (ushort)port;      // 端口
            _client.SocketBufferSize = 4096;  // 缓存4K
            //_client.BindAddress = "";  // 本地绑定到哪个ip
            //_client.BindPort = 0;      // 本地绑定到哪个端口

            // <3> 绑定事件
            //event ClientPrepareConnectEventHandler OnPrepareConnect;  // 准备连接了事件
            //event ClientConnectEventHandler OnConnect;      // 连接事件
            //event ClientHandShakeEventHandler OnHandShake;  // TCP握手成功事件
            //event ClientSendEventHandler OnSend;            // 数据包发送事件
            //event ClientReceiveEventHandler OnReceive;      // 数据包到达事件
            //event ClientCloseEventHandler OnClose;          // TCP连接关闭事件
            _client.OnPrepareConnect += OnPrepareConnect;  // 准备连接了事件
            _client.OnConnect += OnConnect;                // 连接事件
            _client.OnHandShake += OnHandShake;            // TCP握手成功事件
            _client.OnSend += OnSend;                      // 数据包发送事件
            _client.OnReceive += OnReceive;                // 数据包到达事件
            _client.OnClose += OnClose;                    // TCP连接关闭事件

            //_client.Connect();  // 连接
            return _client;
        }
        #region 事件
        /// <summary>
        /// 准备连接了事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socket">客户端Id</param>
        /// <returns></returns>
        private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
        {
            sender.ExtraData = string.Empty;  // 设置附加数据(用来做粘包处理)

            return HandleResult.Ok;
        }
        /// <summary>
        /// 连接事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnConnect(IClient sender)
        {
            //ShowLog(string.Format("TCP客户端([{0}]{1}:{2})接入TCP服务器{3}:{4}", sender.ConnectionId, sender.BindAddress, sender.BindPort, sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IClient sender)
        {
            // 一般用不到
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnSend(IClient sender, byte[] data)
        {
            //ShowLog(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]:{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包到达事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnReceive(IClient sender, byte[] data)
        {
            // <1> 获取附加数据对象
            if (!(sender.ExtraData is string extraDataStr))
            {
                return HandleResult.Error;
            }

            // <2> 将接收数据转换成字符串
            string msg = Encoding.UTF8.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)

            // <3> 显示信息
            //ShowLog(string.Format("TCP客户端接收到TCP服务器[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, msg));

            // <4> 处理数据
            HandleResult result;
            string _endsWith = "\r\n";
            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整,忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <5> 保存PacketData数据
            if (extraDataStr.Length > _client.SocketBufferSize)  // 可选-控制长度为4096(注:实际所占空间大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_client.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.ExtraData = extraDataStr;  // 初始化附加信息

            return result;
        }
        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socketOperation">关闭类型</param>
        /// <param name="errorCode">错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
        {
            sender.ExtraData = null;  // 删除附加数据
            //ShowLog(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
            return HandleResult.Ok;
        }
        #endregion 事件
        #endregion 创建TCPClient并连接示例
        #endregion 发送端(客户端)
    }
}

2、UDP(IUdpArqServer(重点)IUdpArqClient(重点)

  略;HPSocket.Net作者的Demo中有示例,见:Demo/udp/UdpArqServer-TestEchoDemo/udp/UUdpArqClient-TestEcho

三、与线程池一起使用(演示案例为:ITCPPackServer)

  略;HPSocket.Net作者的Demo中有示例,见:Demo/TcpPackServer-TestEchoTcpPackClient-TestEcho

四、HPSocket扩展Easy组件(部分)

  HPSocket.Net目前提供6个Easy组件和2个WebSocket组件

  • ITcpPortForwarding
  • IHttpEasyServer
  • IHttpsEasyServer
  • IHttpEasyAgent
  • IHttpsEasyAgent
  • IHttpEasyClient
  • IHttpsEasyClient
  • IWebSocketServer
  • IWebSocketAgent
  • ITcpServer<TRequestBodyType>
  • ITcpClient<TRequestBodyType>
  • ITcpAgent<TRequestBodyType>
  • ISslServer<TRequestBodyType>
  • ISslClient<TRequestBodyType>
  • ISslAgent<TRequestBodyType>
  • AsyncQueue

1、TCP端口转发(ITcpPortForwarding)

private readonly ITcpPortForwarding _portForwarding = new HPSocket.Tcp.TcpPortForwarding();

 // tcp端口转发组件是支持通过代理连接到目标服务器的, http和socks5代理可以同时混用, 会随机挑选代理服务器, 支持无限多个
  /*
  _portForwarding.ProxyList = new List<IProxy>
  {
      // 支持http隧道代理
      new HttpProxy
      {
          Host = "127.0.0.1",
          Port = 1080,
          // 支持帐号和密码, 可选
          // UserName = "admin",
          // Password = "pass"
      },
      // 也支持socks5代理
      new Socks5Proxy
      {
          Host = "127.0.0.1",
          Port = 1080,
          // 支持帐号和密码, 可选
          // UserName = "admin",
          // Password = "pass"
      }
  };
  */

  // 事件绑定, 如果你不需要处理数据包和连接黑名单等业务, 只是纯做tcp转发, 事件也无需绑定, 直接[启动按钮]事件的代码设置好属性, 然后start()就ok了
  // 转发组件内部server组件的事件
  _portForwarding.OnServerAccept += OnServerAccept;
  _portForwarding.OnServerReceive += OnServerReceive;
  _portForwarding.OnServerClose += OnServerClose;
  // 转发组件内部agent组件的事件
  _portForwarding.OnAgentConnect += OnAgentConnect;
  _portForwarding.OnAgentReceive += OnAgentReceive;
  _portForwarding.OnAgentClose += OnAgentClose;

...
#region 方法
        // ReSharper disable once MemberCanBeMadeStatic.Local
        private HandleResult OnServerReceive(IServer sender, IntPtr connId, byte[] data)
        {
            AddLog($"OnServerReceive({connId}), data length: {data.Length}");
            return HandleResult.Ok;
        }

        // ReSharper disable once MemberCanBeMadeStatic.Local
        private HandleResult OnServerClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            AddLog($"OnServerClose({connId}), socket operation: {socketOperation}, error code: {errorCode}");
            return HandleResult.Ok;
        }

        // ReSharper disable once MemberCanBeMadeStatic.Local
        private HandleResult OnAgentConnect(IAgent sender, IntPtr connId, IProxy proxy)
        {
            AddLog($"OnAgentConnect({connId})");
            return HandleResult.Ok;
        }

        // ReSharper disable once MemberCanBeMadeStatic.Local
        private HandleResult OnAgentReceive(IAgent sender, IntPtr connId, byte[] data)
        {
            AddLog($"OnAgentReceive({connId}), data length: {data.Length}");
            return HandleResult.Ok;
        }

        // ReSharper disable once MemberCanBeMadeStatic.Local
        private HandleResult OnAgentClose(IAgent sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            AddLog($"OnAgentClose({connId}), socket operation: {socketOperation}, error code: {errorCode}");
            return HandleResult.Ok;
        }
#endregion

官方Dome样式

易语言hpsocket教程_net socket框架

2、Http-Easy通讯组件-IHttpEasyServer、IHttpEasyAgent与IHttpEasyClient

// 创建 HttpEasyServer 的实例
using(IHttpEasyServer httpServer = new HttpEasyServer())
{
    // ...其他设置

    // 绑定 OnEasyMessageData 事件
    httpServer.OnEasyMessageData += (sender, id, data) => 
    {
        // data 参数每次都是一个完整的数据包
        // ... 处理 data

        return HttpParseResult.Ok;
    };
}
// 创建 HttpEasyAgent 的实例
using(IHttpEasyAgent httpAgent = new HttpEasyAgent())
{
    // ...其他设置

    // 绑定 OnEasyMessageData 事件
    httpAgent.OnEasyMessageData += (sender, id, data) => 
    {
        // data 参数每次都是一个完整的数据包
        // ... 处理 data

        return HttpParseResult.Ok;
    };
}
// 创建 HttpEasyClient 的实例
using(IHttpEasyClient httpClient = new HttpEasyClient())
{
    // ...其他设置

    // 绑定 OnEasyMessageData 事件
    httpClient.OnEasyMessageData += (sender, data) => 
    {
        // data 参数每次都是一个完整的数据包
        // ... 处理 data

        return HttpParseResult.Ok;
    };
}

3、WebSocket-Easy通讯组件IWebSocketServerIWebSocketAgent

  略

4、TCP数据接收适配器组件(BinaryDataReceiveAdapter-必学

  数据接收适配器组件比HP-Socket的Pack组件更加灵活。  

    • ITcpServer<TRequestBodyType>/ITcpClient<TRequestBodyType>/ITcpAgent<TRequestBodyType>
    • ISslServer<TRequestBodyType>/ISslClient<TRequestBodyType>/ISslAgent<TRequestBodyType>

    注:<TRequestBodyType>泛型类型对象将在‘数据接收适配器组件’的OnParseRequestBody事件中回调。

  4种适配模式:固定包头数据接收适配器、定长包数据接收适配器、结束符数据接收适配器、区间数据接收适配器。以下举例为:ITcpServer<TRequestBodyType> + DataReceiveAdapter定长包数据接收适配器

using (ITcpServer<byte[]> server = new TcpServer<byte[]>
    {
        // 指定数据接收适配器-
        DataReceiveAdapter = new BinaryDataReceiveAdapter(),
    })
{
    // 不需要绑定OnReceive事件
    // 这里解析的请求体事件的data就是BinaryDataReceiveAdapter.ParseRequestBody()返回的数据
    // data的类型就是ITcpServer<byte[]>实例化时指定的byte[]
    server.OnParseRequestBody += (sender, id, data) =>
    {
        Console.WriteLine($"OnParseRequestBody({id}) -> data length: {data.Length}");

        return HandleResult.Ok;
    };
}

(1)FixedHeaderDataReceiveAdapter固定包头数据接收适配器

使用场景:数据包的包头长度固定且包头含包体长度。

示例:前4字节标识包体长度(小端字节序)。0x00000003表示包体长度为3字节{ 0x61, 0x62 0x63 }为包体。

{ 0x03, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63 }

FixedHeaderDataReceiveAdapter专为这种结构设计

using System;
using System.Text;
using HPSocket.Adapter;
using Models;
using Newtonsoft.Json;

namespace TcpServerTestEchoAdapter.DataReceiveAdapter
{
    /// <summary>
    /// 固定包头数据接收适配器
    /// ① 子类继承FixedHeaderDataReceiveAdapter;
    /// ② 在自身的构造函数中调用父类构造函数,传入包头长度和最大允许的封包长度; 
    /// ③ 覆盖GetBodySize()方法, 其参数header的长度为构造函数中指定的包头长度, 需用户解析这个参数, 返回实际的body长度 
    /// ④ 覆盖ParseRequestBody()方法, 将当前的bytes反序列化为泛型类型(<TRequestBodyType>)对象
    /// </summary>
    public class PacketDataReceiveAdapter : FixedHeaderDataReceiveAdapter<Packet>
    {
        /// <summary>
        /// 调用父类构造函数,指定固定包头的长度及最大封包长度
        /// </summary>
        public PacketDataReceiveAdapter()
            : base(
                headerSize: 4,        // 这里指定4字节包头
                maxPacketSize: 0x1000 // 这里指定最大封包长度不能超过4K
                )
        {

        }

        /// <summary>
        /// 获取请求体长度
        /// <remarks>子类必须覆盖此方法</remarks>
        /// </summary>
        /// <param name="header">包头,header的长度是构造函数里指定的长度,当接收到了指定长度的包头再调用此方法</param>
        /// <returns>返回包体长度</returns>
        protected override int GetBodySize(byte[] header)
        {
            // 根据业务场景来适配字节序, 两端字节序要保持一致

            // 如果当前环境不是小端字节序
            if (!BitConverter.IsLittleEndian)
            {
                // 转换为小端字节序
                Array.Reverse(header);
            }

            // 因为包头是4字节,所以直接转int并返回
            return BitConverter.ToInt32(header, 0);
        }

        /// <summary>
        /// 解析请求体
        /// <remarks>子类必须覆盖此方法</remarks>
        /// </summary>
        /// <param name="header">包头</param>
        /// <param name="data">包体</param>
        /// <returns></returns>
        protected override Packet ParseRequestBody(byte[] header, byte[] data)
        {
            // 将data反序列化为对象
            // 这里是Packet类的对象
            return JsonConvert.DeserializeObject<Packet>(Encoding.UTF8.GetString(data));
        }
    }
}

(2)FixedSizeDataReceiveAdapter定长包数据接收适配器

包长度固定,每个包都是同样大小,使用这种适配器。

FixedSizeDataReceiveAdapter专为这种结构设计

using HPSocket.Adapter;

namespace TcpServerTestEchoAdapter.DataReceiveAdapter
{
    /// <summary>
    /// 定长包数据接收适配器
    /// </summary>
    public class BinaryDataReceiveAdapter : FixedSizeDataReceiveAdapter<byte[]>
    {
        /// <summary>
        /// 调用父类构造函数,指定定长包长度
        /// </summary>
        public BinaryDataReceiveAdapter()
            : base(
                packetSize: 1024 // 定长包包长1K字节
            )
        {
        }

        /// <summary>
        /// 解析请求体
        /// <remarks>子类必须覆盖此方法</remarks>
        /// </summary>
        /// <param name="data">父类处理好的定长数据</param>
        /// <returns></returns>
        protected override byte[] ParseRequestBody(byte[] data)
        {
            // 因为继承自FixedSizeDataReceiveAdapter<byte[]>,所以这里直接返回了,如果是其他类型请做完转换工作再返回
            return data;
        }
    }
}

(3)TerminatorDataReceiveAdapter结束符数据接收适配器

包头无特征, 包尾使用特定标志作为结束符,使用这种适配器

示例:下面这种包结构以\r\n结尾

hello world 1\r\n
hello world 2\r\n
hello world 3\r\n

TerminatorDataReceiveAdapter专为这种结构设计

using System.Text;
using HPSocket.Adapter;

namespace TcpServerTestEchoAdapter.DataReceiveAdapter
{
    /// <summary>
    /// 结束符数据接收适配器
    /// </summary>
    public class TextDataReceiveAdapter : TerminatorDataReceiveAdapter<string>
    {
        /// <summary>
        /// 调用父类构造函数,指定结束符
        /// </summary>
        public TextDataReceiveAdapter()
            : base(
                terminator: Encoding.UTF8.GetBytes("\r\n") // 指定结束符为\r\n,也就是说每条数据以\r\n结尾,注意编码问题,要两端保持一致
                )
        {
        }

        /// <summary>
        /// 解析请求体
        /// <remarks>子类必须覆盖此方法</remarks>
        /// </summary>
        /// <param name="data">父类已经处理好的不含结束符的数据</param>
        /// <returns></returns>
        protected override string ParseRequestBody(byte[] data)
        {
            // 转换成请求对象, 注意字符编码,要两端保持一致
            return Encoding.UTF8.GetString(data);
        }
    }
}

(4)BetweenAndDataReceiveAdapter区间数据接收适配器

示例:数据包头以某特征符号开始,包尾以其它某特征符号结束

##hello world!## // ##开头,##结尾
或
##hello world!|| // ##开头,||结尾
或
**hello world!|##| // **开头,|##|结尾

BetweenAndDataReceiveAdapter专为这种结构设计

using System.Text;
using HPSocket.Adapter;

namespace TcpServerTestEchoAdapter.DataReceiveAdapter
{
    /// <summary>
    /// 区间数据接收适配器
    /// </summary>
    public class HeadTailDataReceiveAdapter : BetweenAndDataReceiveAdapter<string>
    {
        /// <summary>
        /// 调用父类构造函数,指定区间开始和结束特征字符
        /// </summary>
        public HeadTailDataReceiveAdapter() 
            : base( // 例如数据格式是"#*123456*#",其中以#*开头,以*#结尾,中间123456部分是真实数据
                start : Encoding.UTF8.GetBytes("#*"), // 区间起始标志,这里以#*开始,注意编码问题,要两端保持一致
                end : Encoding.UTF8.GetBytes("*#")  // 区间结束标志,这里以*#结束,注意编码问题,要两端保持一致
                )
        {
        }

        /// <summary>
        /// 解析请求体
        /// <remarks>子类必须覆盖此方法</remarks>
        /// </summary>
        /// <param name="data">父类已处理好的不含区间起始标识符的数据</param>
        /// <returns></returns>
        protected override string ParseRequestBody(byte[] data)
        {
            // 转换成请求对象,注意编码问题,要两端保持一致
            return Encoding.UTF8.GetString(data);
        }
    }
}

补充:

1、Event select模型:

  select模型主要用于解决传统tcp通信线程过多的问题,而EventSelect模型则用于解决select模型的效率问题,因为select模型的内部是使用Sleep函数来阻塞线程,然后消耗系统时间片,从而降低了效率,而event+select模型则使用WSAEVENT的通知机制。

2、IOCP 通信模型:

  IOCP(I/O Completion Port),常称I/O完成端口。 IOCP模型属于一种通讯模型,适用于能控制并发执行的高负载服务器的一个技术。 通俗一点说,就是用于高效处理很多很多的客户端进行数据交换的一个模型。或者可以说,就是能异步I/O操作的模型。优化线程池调度,提高CPU和内存缓冲的命中率。

今天的文章易语言hpsocket教程_net socket框架分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/51141.html

(0)
编程小号编程小号
上一篇 2023-09-01
下一篇 2023-09-01

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注