一、序言
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 组件采用相同的技术架构,可以用作代理服务器或中转服务器的客户端部件。
(6)官方示例地址: https://gitee.com/int2e/HPSocket.Net/tree/develop/demo
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演示
- 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
这里我演示TCPServer
、TCPClient
基础类的使用,代码地址: csharp_networkprotocol_hpsocket;在平时使用中,推荐下面两种方案:
- “
ITcpServer<TRequestBodyType>
或ITcpClient<TRequestBodyType>
或ITcpAgent<TRequestBodyType>
”+“TCP数据接收适配器组件(BinaryDataReceiveAdapter)
”; - “
ITcpPackServer
或ITcpPackClient
或ITcpPackAgent
”;
(1)TcpServer使用示例
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;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
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 操控页面控件
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
(2)TcpClient使用示例
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;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
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 方法
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
(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 发送端(客户端)
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.
- 472.
- 473.
- 474.
- 475.
- 476.
- 477.
- 478.
- 479.
- 480.
- 481.
- 482.
- 483.
- 484.
- 485.
- 486.
- 487.
- 488.
- 489.
- 490.
- 491.
- 492.
- 493.
- 494.
- 495.
- 496.
- 497.
- 498.
- 499.
- 500.
- 501.
- 502.
- 503.
- 504.
- 505.
- 506.
- 507.
- 508.
- 509.
- 510.
- 511.
- 512.
- 513.
- 514.
- 515.
- 516.
- 517.
- 518.
- 519.
- 520.
- 521.
- 522.
- 523.
- 524.
- 525.
- 526.
- 527.
- 528.
- 529.
- 530.
- 531.
- 532.
- 533.
- 534.
- 535.
- 536.
- 537.
- 538.
- 539.
- 540.
- 541.
- 542.
- 543.
- 544.
- 545.
- 546.
- 547.
- 548.
- 549.
- 550.
- 551.
- 552.
- 553.
- 554.
- 555.
- 556.
- 557.
- 558.
- 559.
- 560.
- 561.
- 562.
- 563.
- 564.
- 565.
- 566.
- 567.
- 568.
- 569.
- 570.
- 571.
- 572.
- 573.
- 574.
- 575.
- 576.
- 577.
- 578.
- 579.
- 580.
- 581.
- 582.
- 583.
- 584.
- 585.
- 586.
- 587.
- 588.
- 589.
- 590.
- 591.
- 592.
- 593.
- 594.
- 595.
- 596.
- 597.
- 598.
- 599.
- 600.
- 601.
- 602.
- 603.
- 604.
- 605.
- 606.
- 607.
- 608.
- 609.
- 610.
- 611.
- 612.
- 613.
- 614.
- 615.
- 616.
- 617.
- 618.
- 619.
- 620.
- 621.
- 622.
- 623.
- 624.
- 625.
- 626.
- 627.
- 628.
- 629.
- 630.
- 631.
- 632.
- 633.
- 634.
- 635.
- 636.
- 637.
- 638.
- 639.
- 640.
- 641.
- 642.
- 643.
- 644.
- 645.
- 646.
- 647.
- 648.
- 649.
- 650.
- 651.
- 652.
- 653.
- 654.
- 655.
- 656.
- 657.
- 658.
- 659.
- 660.
- 661.
- 662.
- 663.
- 664.
- 665.
- 666.
- 667.
- 668.
- 669.
- 670.
- 671.
- 672.
- 673.
- 674.
- 675.
- 676.
- 677.
- 678.
- 679.
- 680.
- 681.
- 682.
- 683.
- 684.
- 685.
- 686.
- 687.
- 688.
- 689.
- 690.
- 691.
- 692.
- 693.
- 694.
- 695.
- 696.
- 697.
- 698.
- 699.
- 700.
- 701.
- 702.
- 703.
- 704.
- 705.
- 706.
- 707.
- 708.
- 709.
- 710.
- 711.
- 712.
- 713.
- 714.
- 715.
- 716.
- 717.
- 718.
- 719.
- 720.
- 721.
- 722.
- 723.
- 724.
- 725.
- 726.
- 727.
- 728.
- 729.
- 730.
- 731.
- 732.
- 733.
- 734.
- 735.
- 736.
- 737.
- 738.
- 739.
- 740.
- 741.
- 742.
- 743.
- 744.
- 745.
- 746.
- 747.
- 748.
- 749.
- 750.
- 751.
- 752.
- 753.
- 754.
- 755.
- 756.
- 757.
- 758.
- 759.
- 760.
- 761.
- 762.
- 763.
- 764.
- 765.
- 766.
- 767.
- 768.
- 769.
- 770.
- 771.
- 772.
- 773.
- 774.
- 775.
- 776.
- 777.
- 778.
- 779.
- 780.
- 781.
- 782.
- 783.
- 784.
- 785.
- 786.
- 787.
- 788.
- 789.
- 790.
- 791.
- 792.
- 793.
- 794.
- 795.
- 796.
- 797.
- 798.
- 799.
- 800.
- 801.
- 802.
- 803.
- 804.
- 805.
- 806.
- 807.
- 808.
- 809.
- 810.
- 811.
- 812.
- 813.
- 814.
- 815.
- 816.
- 817.
- 818.
- 819.
- 820.
- 821.
- 822.
- 823.
- 824.
- 825.
- 826.
- 827.
- 828.
- 829.
- 830.
- 831.
- 832.
- 833.
- 834.
- 835.
- 836.
- 837.
- 838.
- 839.
- 840.
- 841.
- 842.
- 843.
- 844.
- 845.
- 846.
- 847.
- 848.
- 849.
- 850.
- 851.
- 852.
- 853.
- 854.
- 855.
- 856.
- 857.
- 858.
- 859.
- 860.
- 861.
- 862.
- 863.
- 864.
- 865.
- 866.
- 867.
- 868.
- 869.
- 870.
- 871.
- 872.
- 873.
- 874.
- 875.
- 876.
- 877.
- 878.
- 879.
- 880.
- 881.
- 882.
- 883.
- 884.
- 885.
- 886.
- 887.
- 888.
- 889.
- 890.
- 891.
- 892.
- 893.
- 894.
- 895.
- 896.
- 897.
- 898.
- 899.
- 900.
- 901.
- 902.
- 903.
- 904.
- 905.
- 906.
- 907.
- 908.
- 909.
- 910.
- 911.
- 912.
- 913.
- 914.
- 915.
- 916.
- 917.
- 918.
- 919.
- 920.
- 921.
- 922.
- 923.
- 924.
- 925.
- 926.
- 927.
- 928.
- 929.
- 930.
- 931.
- 932.
- 933.
- 934.
- 935.
- 936.
- 937.
- 938.
- 939.
- 940.
- 941.
- 942.
- 943.
- 944.
- 945.
- 946.
- 947.
- 948.
- 949.
- 950.
- 951.
- 952.
- 953.
- 954.
- 955.
- 956.
- 957.
- 958.
- 959.
- 960.
- 961.
- 962.
- 963.
- 964.
- 965.
- 966.
- 967.
- 968.
- 969.
- 970.
- 971.
- 972.
- 973.
- 974.
- 975.
- 976.
- 977.
- 978.
- 979.
- 980.
- 981.
- 982.
- 983.
- 984.
- 985.
- 986.
- 987.
- 988.
- 989.
- 990.
- 991.
- 992.
- 993.
- 994.
- 995.
- 996.
- 997.
- 998.
- 999.
- 1000.
- 1001.
- 1002.
- 1003.
- 1004.
- 1005.
- 1006.
- 1007.
- 1008.
- 1009.
- 1010.
- 1011.
- 1012.
- 1013.
- 1014.
- 1015.
- 1016.
- 1017.
- 1018.
- 1019.
- 1020.
- 1021.
- 1022.
- 1023.
- 1024.
- 1025.
- 1026.
- 1027.
- 1028.
- 1029.
- 1030.
- 1031.
- 1032.
- 1033.
- 1034.
- 1035.
- 1036.
- 1037.
- 1038.
- 1039.
- 1040.
- 1041.
- 1042.
- 1043.
- 1044.
- 1045.
- 1046.
- 1047.
- 1048.
- 1049.
- 1050.
- 1051.
- 1052.
- 1053.
- 1054.
- 1055.
- 1056.
- 1057.
- 1058.
- 1059.
- 1060.
- 1061.
- 1062.
- 1063.
- 1064.
- 1065.
- 1066.
- 1067.
- 1068.
- 1069.
- 1070.
- 1071.
- 1072.
- 1073.
- 1074.
- 1075.
- 1076.
- 1077.
- 1078.
- 1079.
- 1080.
- 1081.
- 1082.
- 1083.
- 1084.
- 1085.
- 1086.
- 1087.
- 1088.
- 1089.
- 1090.
- 1091.
- 1092.
- 1093.
- 1094.
- 1095.
- 1096.
- 1097.
- 1098.
- 1099.
- 1100.
- 1101.
- 1102.
- 1103.
- 1104.
- 1105.
- 1106.
- 1107.
- 1108.
- 1109.
- 1110.
- 1111.
- 1112.
- 1113.
- 1114.
- 1115.
- 1116.
- 1117.
- 1118.
- 1119.
- 1120.
- 1121.
- 1122.
- 1123.
- 1124.
- 1125.
- 1126.
- 1127.
- 1128.
- 1129.
2、UDP(IUdpArqServer(重点)
与IUdpArqClient(重点)
)
略;HPSocket.Net作者的Demo中有示例,见: Demo/udp/UdpArqServer-TestEcho与Demo/udp/UUdpArqClient-TestEcho
三、与线程池一起使用(演示案例为:ITCPPackServer)
略;HPSocket.Net作者的Demo中有示例,见: Demo/TcpPackServer-TestEcho与TcpPackClient-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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
官方Dome样式
2、Http-Easy通讯组件-IHttpEasyServer、IHttpEasyAgent与IHttpEasyClient
3、WebSocket-Easy通讯组件IWebSocketServer
与IWebSocketAgent
略
4、TCP数据接收适配器组件(BinaryDataReceiveAdapter-必学)
数据接收适配器
组件比HP-Socket的Pack
组件更加灵活。
ITcpServer<TRequestBodyType>
/ITcpClient<TRequestBodyType>
/ITcpAgent<TRequestBodyType>
ISslServer<TRequestBodyType>
/ISslClient<TRequestBodyType>
/ISslAgent<TRequestBodyType>
注:<TRequestBodyType>
泛型类型对象将在‘数据接收适配器组件’的OnParseRequestBody
事件中回调。
以下举例为: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.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
(1)FixedHeaderDataReceiveAdapter
固定包头数据接收适配器
使用场景:数据包的包头长度固定且包头含包体长度。
示例:前4字节
标识包体长度(小端字节序)。0x00000003
表示包体长度为3字节
,{ 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));
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
(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;
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
(3)TerminatorDataReceiveAdapter
结束符数据接收适配器
包头无特征, 包尾使用特定标志作为结束符,使用这种适配器
示例:下面这种包结构以\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);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
(4)BetweenAndDataReceiveAdapter
区间数据接收适配器
示例:数据包头以某特征符号开始,包尾以其它某特征符号结束
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.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
补充:
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和内存缓冲的命中率。
作者:꧁执笔小白꧂