123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152 |
- /**
- *┌──────────────────────────────────────────────────────────────┐
- *│ 描 述:HPSocket的TCPServer、TCPClient通讯相关的工具类(类库提供的方法、属性学习)
- *│ 作 者:执笔小白
- *│ 版 本:1.0
- *│ 创建时间:2023-6-13 10:40:56
- *└──────────────────────────────────────────────────────────────┘
- *┌──────────────────────────────────────────────────────────────┐
- *│ 命名空间: ZhibiXiaobai
- *│ 类 名:HPSocket_TCPServerHelper、HPSocket_TcpClientHelper
- *└──────────────────────────────────────────────────────────────┘
- */
- using HPSocket;
- using HPSocket.Tcp;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Text;
- 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 { return _server.Address; }
- set { _server.Address = value; }
- }
- /// <summary>
- /// 要绑定的服务器端口
- /// </summary>
- public ushort Port
- {
- get
- {
- return _server.Port;
- }
- set { _server.Port = value; }
- }
- /// <summary>
- /// 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)
- /// </summary>
- public uint MaxConnectionCount
- {
- get
- {
- return _server.MaxConnectionCount;
- }
- set { _server.MaxConnectionCount = value; }
- }
- /// <summary>
- /// 读取或设置是否标记静默时间
- /// (设置为 true 时 DisconnectSilenceConnections() 和 GetSilencePeriod()才有效,默认:false)
- /// </summary>
- public bool IsMarkSilence
- {
- get { return _server.IsMarkSilence; }
- set { _server.IsMarkSilence = value; }
- }
- /// <summary>
- /// 获取或设置数据发送策略
- /// </summary>
- public SendPolicy SendPolicy
- {
- get {return _server.SendPolicy; }
- set { _server.SendPolicy = value; }
- }
- /// <summary>
- /// 获取或设置 OnSend 事件同步策略
- /// </summary>
- public OnSendSyncPolicy OnSendSyncPolicy
- {
- get
- {
- return _server.OnSendSyncPolicy;
- }
- set { _server.OnSendSyncPolicy = value; }
- }
- /// <summary>
- /// 获取或设置地址重用选项
- /// </summary>
- public ReuseAddressPolicy ReuseAddressPolicy
- {
- get {return _server.ReuseAddressPolicy; }
- set { _server.ReuseAddressPolicy = value; }
- }
- /// <summary>
- /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
- /// </summary>
- public bool NoDelay
- {
- get {return _server.NoDelay; }
- set { _server.NoDelay = value; }
- }
- #region 心跳检测
- /// <summary>
- /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
- /// </summary>
- public uint KeepAliveTime
- {
- get {return _server.KeepAliveTime; }
- set { _server.KeepAliveTime = value;
- }
- }
- /// <summary>
- /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
- /// </summary>
- public uint KeepAliveInterval
- {
- get
- {
- return _server.KeepAliveInterval;
- }
- set { _server.KeepAliveInterval = value; }
- }
- #endregion 心跳检测
- #region 性能优化
- /// <summary>
- /// 读取或设置 Accept 预投递数量(根据负载调整设置,Accept 预投递数量越大则支持的并发连接请求越多)
- /// </summary>
- public uint AcceptSocketCount
- {
- get { return _server.AcceptSocketCount; }
- set { _server.AcceptSocketCount = value; }
- }
- /// <summary>
- /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数)
- /// </summary>
- public uint SocketBufferSize
- {
- get
- {
- return _server.SocketBufferSize;
- }
- set { _server.SocketBufferSize = value; }
- }
- /// <summary>
- /// 读取或设置监听 Socket 的等候队列大小(根据并发连接数量调整设置)
- /// </summary>
- public uint SocketListenQueue
- {
- get { return _server.SocketListenQueue; }
- set { _server.SocketListenQueue = value; }
- }
- /// <summary>
- /// 读取或设置工作线程数量(通常设置为 2 * CPU + 2)
- /// </summary>
- public uint WorkerThreadCount
- {
- get { return _server.WorkerThreadCount; }
- set { _server.WorkerThreadCount = value; }
- }
- /// <summary>
- /// 读取或设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用)
- /// </summary>
- public uint FreeSocketObjLockTime
- {
- get { return _server.FreeSocketObjLockTime; }
- set { _server.FreeSocketObjLockTime = value; }
- }
- /// <summary>
- /// 读取或设置 Socket 缓存池大小(通常设置为平均并发连接数量的 1/3 - 1/2)
- /// </summary>
- public uint FreeSocketObjPool
- {
- get { return _server.FreeSocketObjPool; }
- set { _server.FreeSocketObjPool = value; }
- }
- /// <summary>
- /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
- /// </summary>
- public uint FreeBufferObjPool
- {
- get { return _server.FreeBufferObjPool; }
- set { _server.FreeBufferObjPool = value; }
- }
- /// <summary>
- /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
- /// </summary>
- public uint FreeSocketObjHold
- {
- get { return _server.FreeSocketObjHold; }
- set { _server.FreeSocketObjHold = value; }
- }
- /// <summary>
- /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
- /// </summary>
- public uint FreeBufferObjHold
- {
- get { return _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)
- {
- // 获取客户端地址
- string ip = string.Empty;
- ushort port = 0;
- if (!sender.GetRemoteAddress(connId, out ip, out 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)
- {
- string ip = string.Empty;
- ushort port = 0;
- sender.GetRemoteAddress(connId, out ip, out 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> 获取客户端地址
- string ip = string.Empty;
- ushort port = 0;
- if (!sender.GetRemoteAddress(connId, out ip, out 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> 获取客户端地址
- string ip = string.Empty;
- ushort port = 0;
- if (!sender.GetRemoteAddress(connId, out ip, out 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 { return _client.BindAddress; }
- set { _client.BindAddress = value; }
- }
- /// <summary>
- /// 本地绑定到哪个端口
- /// </summary>
- public ushort BindPort
- {
- get { return _client.BindPort; }
- set { _client.BindPort = value; }
- }
- /// <summary>
- /// 远程服务器地址
- /// </summary>
- public string Address
- {
- get { return _client.Address; }
- set { _client.Address = value; }
- }
- /// <summary>
- /// 远程服务器端口
- /// </summary>
- public ushort Port
- {
- get { return _client.Port; }
- set { _client.Port = value; }
- }
- #region 创建时的设置项
- /// <summary>
- /// 是否异步连接,默认为真
- /// </summary>
- public bool Async
- {
- get { return _client.Async; }
- set { _client.Async = value; }
- }
- /// <summary>
- /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为:(N * 1024) - sizeof(TBufferObj))
- /// </summary>
- public uint SocketBufferSize
- {
- get { return _client.SocketBufferSize; }
- set { _client.SocketBufferSize = value; }
- }
- /// <summary>
- /// 连接超时时间, 默认操作系统默认值
- /// 单位: 毫秒
- /// 异常:T:System.InvalidOperationException:同步连接、.NET Framework2.0以及设置小于100毫秒会引发此异常
- /// </summary>
- public int ConnectionTimeout
- {
- get { return _client.ConnectionTimeout; }
- set { _client.ConnectionTimeout = value; }
- }
- /// <summary>
- /// 附加数据
- /// 赋值:client.ExtraData = myObj;
- /// 取值:var data = ExtraData as MyData;
- /// </summary>
- public object ExtraData
- {
- get { return _client.ExtraData; }
- set { _client.ExtraData = value; }
- }
- /// <summary>
- /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
- /// </summary>
- public bool NoDelay
- {
- get { return _client.NoDelay; }
- set { _client.NoDelay = value; }
- }
- #region 心跳
- /// <summary>
- /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
- /// </summary>
- public uint KeepAliveTime
- {
- get { return _client.KeepAliveTime; }
- set { _client.KeepAliveTime = value; }
- }
- /// <summary>
- /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
- /// </summary>
- public uint KeepAliveInterval
- {
- get { return _client.KeepAliveInterval; }
- set { _client.KeepAliveInterval = value; }
- }
- #endregion 心跳
- /// <summary>
- /// 获取或设置暂停接收状态,设置状态时,不允许设置为ReceiveState.Unknown,
- /// </summary>
- public ReceiveState PauseReceive
- {
- get { return _client.PauseReceive; }
- set { _client.PauseReceive = value; }
- }
- /// <summary>
- /// 获取或设置地址重用选项
- /// </summary>
- public ReuseAddressPolicy ReuseAddressPolicy
- {
- get { return _client.ReuseAddressPolicy; }
- set { _client.ReuseAddressPolicy = value; }
- }
- /// <summary>
- /// 代理列表
- /// </summary>
- public List<IProxy> ProxyList
- {
- get { return _client.ProxyList; }
- set { _client.ProxyList = value; }
- }
- #region 性能优化
- /// <summary>
- /// 读取或设置内存块缓存池大小(通常设置为 -> PUSH 模型:5 - 10;PULL 模型:10 - 20 )
- /// </summary>
- public uint FreeBufferPoolSize
- {
- get { return _client.FreeBufferPoolSize; }
- set { _client.FreeBufferPoolSize = value; }
- }
- /// <summary>
- /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
- /// </summary>
- public uint FreeBufferPoolHold
- {
- get { return _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))
- {
- return HandleResult.Error;
- }
- string extraDataStr = (string)sender.ExtraData;
- // <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 发送端(客户端)
- }
- }
|