一、序言
1、HPSocket.Net介绍
2、HPSocket.Net相对于System.Net.Sockets具有的优势
3、与HPSocket差不多的Socket框架
二、基础使用
代码地址:csharp_networkprotocol_hpsocket
- 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;
}
}
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使用示例
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-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
官方Dome样式
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通讯组件IWebSocketServer
与IWebSocketAgent
略
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和内存缓冲的命中率。
本文来自博客园,作者:꧁执笔小白꧂,转载请注明原文链接:https://www.cnblogs.com/qq2806933146xiaobai/p/17476671.html
今天的文章易语言hpsocket教程_net socket框架分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/51141.html