HPSocket_TCPHelper.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  1. /**
  2. *┌──────────────────────────────────────────────────────────────┐
  3. *│ 描 述:HPSocket的TCPServer、TCPClient通讯相关的工具类(类库提供的方法、属性学习)
  4. *│ 作 者:执笔小白
  5. *│ 版 本:1.0
  6. *│ 创建时间:2023-6-13 10:40:56
  7. *└──────────────────────────────────────────────────────────────┘
  8. *┌──────────────────────────────────────────────────────────────┐
  9. *│ 命名空间: ZhibiXiaobai
  10. *│ 类 名:HPSocket_TCPServerHelper、HPSocket_TcpClientHelper
  11. *└──────────────────────────────────────────────────────────────┘
  12. */
  13. using HPSocket;
  14. using HPSocket.Tcp;
  15. using System;
  16. using System.Collections.Concurrent;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using System.Net;
  20. using System.Text;
  21. namespace csharp_networkprotocol_hpsocket
  22. {
  23. /// <summary>
  24. /// HPSocket_TCPServer示例帮助类(类库提供的方法、属性学习)
  25. /// System.Net.Sockets类库
  26. /// TcpServer
  27. /// </summary>
  28. public class HPSocket_TCPServerHelper
  29. {
  30. #region 接收端(服务端)
  31. /// <summary>
  32. /// 侦听来自 TCP 网络客户端的连接
  33. /// </summary>
  34. public ITcpServer _server = null;
  35. #region 创建时的设置项
  36. /// <summary>
  37. /// 要绑定的服务器地址
  38. /// </summary>
  39. public string Address
  40. {
  41. get { return _server.Address; }
  42. set { _server.Address = value; }
  43. }
  44. /// <summary>
  45. /// 要绑定的服务器端口
  46. /// </summary>
  47. public ushort Port
  48. {
  49. get
  50. {
  51. return _server.Port;
  52. }
  53. set { _server.Port = value; }
  54. }
  55. /// <summary>
  56. /// 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)
  57. /// </summary>
  58. public uint MaxConnectionCount
  59. {
  60. get
  61. {
  62. return _server.MaxConnectionCount;
  63. }
  64. set { _server.MaxConnectionCount = value; }
  65. }
  66. /// <summary>
  67. /// 读取或设置是否标记静默时间
  68. /// (设置为 true 时 DisconnectSilenceConnections() 和 GetSilencePeriod()才有效,默认:false)
  69. /// </summary>
  70. public bool IsMarkSilence
  71. {
  72. get { return _server.IsMarkSilence; }
  73. set { _server.IsMarkSilence = value; }
  74. }
  75. /// <summary>
  76. /// 获取或设置数据发送策略
  77. /// </summary>
  78. public SendPolicy SendPolicy
  79. {
  80. get {return _server.SendPolicy; }
  81. set { _server.SendPolicy = value; }
  82. }
  83. /// <summary>
  84. /// 获取或设置 OnSend 事件同步策略
  85. /// </summary>
  86. public OnSendSyncPolicy OnSendSyncPolicy
  87. {
  88. get
  89. {
  90. return _server.OnSendSyncPolicy;
  91. }
  92. set { _server.OnSendSyncPolicy = value; }
  93. }
  94. /// <summary>
  95. /// 获取或设置地址重用选项
  96. /// </summary>
  97. public ReuseAddressPolicy ReuseAddressPolicy
  98. {
  99. get {return _server.ReuseAddressPolicy; }
  100. set { _server.ReuseAddressPolicy = value; }
  101. }
  102. /// <summary>
  103. /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
  104. /// </summary>
  105. public bool NoDelay
  106. {
  107. get {return _server.NoDelay; }
  108. set { _server.NoDelay = value; }
  109. }
  110. #region 心跳检测
  111. /// <summary>
  112. /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
  113. /// </summary>
  114. public uint KeepAliveTime
  115. {
  116. get {return _server.KeepAliveTime; }
  117. set { _server.KeepAliveTime = value;
  118. }
  119. }
  120. /// <summary>
  121. /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
  122. /// </summary>
  123. public uint KeepAliveInterval
  124. {
  125. get
  126. {
  127. return _server.KeepAliveInterval;
  128. }
  129. set { _server.KeepAliveInterval = value; }
  130. }
  131. #endregion 心跳检测
  132. #region 性能优化
  133. /// <summary>
  134. /// 读取或设置 Accept 预投递数量(根据负载调整设置,Accept 预投递数量越大则支持的并发连接请求越多)
  135. /// </summary>
  136. public uint AcceptSocketCount
  137. {
  138. get { return _server.AcceptSocketCount; }
  139. set { _server.AcceptSocketCount = value; }
  140. }
  141. /// <summary>
  142. /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数)
  143. /// </summary>
  144. public uint SocketBufferSize
  145. {
  146. get
  147. {
  148. return _server.SocketBufferSize;
  149. }
  150. set { _server.SocketBufferSize = value; }
  151. }
  152. /// <summary>
  153. /// 读取或设置监听 Socket 的等候队列大小(根据并发连接数量调整设置)
  154. /// </summary>
  155. public uint SocketListenQueue
  156. {
  157. get { return _server.SocketListenQueue; }
  158. set { _server.SocketListenQueue = value; }
  159. }
  160. /// <summary>
  161. /// 读取或设置工作线程数量(通常设置为 2 * CPU + 2)
  162. /// </summary>
  163. public uint WorkerThreadCount
  164. {
  165. get { return _server.WorkerThreadCount; }
  166. set { _server.WorkerThreadCount = value; }
  167. }
  168. /// <summary>
  169. /// 读取或设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用)
  170. /// </summary>
  171. public uint FreeSocketObjLockTime
  172. {
  173. get { return _server.FreeSocketObjLockTime; }
  174. set { _server.FreeSocketObjLockTime = value; }
  175. }
  176. /// <summary>
  177. /// 读取或设置 Socket 缓存池大小(通常设置为平均并发连接数量的 1/3 - 1/2)
  178. /// </summary>
  179. public uint FreeSocketObjPool
  180. {
  181. get { return _server.FreeSocketObjPool; }
  182. set { _server.FreeSocketObjPool = value; }
  183. }
  184. /// <summary>
  185. /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
  186. /// </summary>
  187. public uint FreeBufferObjPool
  188. {
  189. get { return _server.FreeBufferObjPool; }
  190. set { _server.FreeBufferObjPool = value; }
  191. }
  192. /// <summary>
  193. /// 读取或设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍)
  194. /// </summary>
  195. public uint FreeSocketObjHold
  196. {
  197. get { return _server.FreeSocketObjHold; }
  198. set { _server.FreeSocketObjHold = value; }
  199. }
  200. /// <summary>
  201. /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
  202. /// </summary>
  203. public uint FreeBufferObjHold
  204. {
  205. get { return _server.FreeBufferObjHold; }
  206. set { _server.FreeBufferObjHold = value; }
  207. }
  208. #endregion 性能优化
  209. #endregion 创建时的设置项
  210. #region 常用属性
  211. /// <summary>
  212. /// 获取是否启动
  213. /// </summary>
  214. public bool HasStarted => _server.HasStarted;
  215. /// <summary>
  216. /// 获取状态
  217. /// </summary>
  218. public ServiceState State => _server.State;
  219. /// <summary>
  220. /// 获取连接数
  221. /// </summary>
  222. public uint ConnectionCount => _server.ConnectionCount;
  223. /// <summary>
  224. /// 是否为安全连接(SSL/HTTPS)
  225. /// </summary>
  226. public bool IsSecure => _server.IsSecure;
  227. #endregion 常用属性
  228. #region 常用方法
  229. /// <summary>
  230. /// 启动服务
  231. /// </summary>
  232. /// <returns></returns>
  233. public bool Start() => _server.Start();
  234. /// <summary>
  235. /// 停止服务
  236. /// </summary>
  237. /// <returns></returns>
  238. public bool Stop() => _server.Stop();
  239. #region 附加信息
  240. /// <summary>
  241. /// 获取所有附加数据
  242. /// </summary>
  243. /// <returns></returns>
  244. public ConcurrentDictionary<IntPtr, object> GetAllExtra() => _server.GetAllExtra();
  245. /// <summary>
  246. /// 获取附加数据
  247. /// </summary>
  248. /// <typeparam name="T"></typeparam>
  249. /// <param name="connId"></param>
  250. /// <returns></returns>
  251. public T GetExtra<T>(IntPtr connId) => _server.GetExtra<T>(connId);
  252. /// <summary>
  253. /// 获取连接附加数据, 非托管版本, hp-socket自带方法;非特殊需求不要使用这个方法, 请直接使用 GetExtra();
  254. /// </summary>
  255. /// <param name="connId"></param>
  256. /// <param name="extra"></param>
  257. /// <returns></returns>
  258. public bool NativeGetConnectionExtra(IntPtr connId, out IntPtr extra) => _server.NativeGetConnectionExtra(connId, out extra);
  259. /// <summary>
  260. /// 设置附加数据
  261. /// </summary>
  262. /// <param name="connId"></param>
  263. /// <param name="obj"></param>
  264. /// <returns></returns>
  265. public bool SetExtra(IntPtr connId, object obj) => _server.SetExtra(connId, obj);
  266. /// <summary>
  267. /// 设置连接附加数据, 非托管版本, hp-socket自带方法;非特殊需求不要使用这个方法, 请直接使用 SetExtra();
  268. /// </summary>
  269. /// <param name="connId"></param>
  270. /// <param name="extra"></param>
  271. /// <returns></returns>
  272. public bool NativeSetConnectionExtra(IntPtr connId, IntPtr extra) => _server.NativeSetConnectionExtra(connId, extra);
  273. /// <summary>
  274. /// 删除附加数据
  275. /// </summary>
  276. /// <param name="connId"></param>
  277. /// <returns></returns>
  278. public bool RemoveExtra(IntPtr connId) => _server.RemoveExtra(connId);
  279. #endregion 附加信息
  280. #region 发送数据
  281. /// <summary>
  282. /// 发送数据
  283. /// </summary>
  284. /// <param name="connId">连接id</param>
  285. /// <param name="bytes">数据源</param>
  286. /// <param name="length">数据长度</param>
  287. /// <returns></returns>
  288. public bool Send(IntPtr connId, byte[] bytes, int length) => _server.Send(connId, bytes, length);
  289. /// <summary>
  290. /// 发送数据-指针偏移
  291. /// </summary>
  292. /// <param name="connId"></param>
  293. /// <param name="bytes"></param>
  294. /// <param name="offset">对bytes的偏移</param>
  295. /// <param name="length">发多大</param>
  296. /// <returns></returns>
  297. public bool Send(IntPtr connId, byte[] bytes, int offset, int length) => _server.Send(connId, bytes, offset, length);
  298. /// <summary>
  299. /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
  300. /// </summary>
  301. /// <param name="connId">连接 ID</param>
  302. /// <param name="buffers">发送缓冲区数组</param>
  303. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  304. public bool SendPackets(IntPtr connId, Wsabuf[] buffers) => SendPackets(connId, buffers);
  305. #endregion 发送数据
  306. #region 发送本地小文件
  307. /// <summary>
  308. /// 发送本地小文件
  309. /// 向指定连接发送 4096 KB 以下的小文件
  310. /// </summary>
  311. /// <param name="connId"></param>
  312. /// <param name="filePath">文件路径</param>
  313. /// <param name="head">头部附加数据</param>
  314. /// <param name="tail">尾部附加数据</param>
  315. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  316. public bool SendSmallFile(IntPtr connId, string filePath, ref Wsabuf head, ref Wsabuf tail) => _server.SendSmallFile(connId, filePath, ref head, ref tail);
  317. /// <summary>
  318. /// 发送本地小文件
  319. /// 向指定连接发送 4096 KB 以下的小文件
  320. /// </summary>
  321. /// <param name="connId"></param>
  322. /// <param name="filePath">文件路径</param>
  323. /// <param name="head">头部附加数据,可以为null</param>
  324. /// <param name="tail">尾部附加数据,可以为null</param>
  325. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  326. public bool SendSmallFile(IntPtr connId, string filePath, byte[] head, byte[] tail) => _server.SendSmallFile(connId, filePath, head, tail);
  327. #endregion 发送本地小文件
  328. #region 查询服务器信息
  329. /// <summary>
  330. /// 获取监听socket的地址信息
  331. /// </summary>
  332. /// <param name="ip"></param>
  333. /// <param name="port"></param>
  334. /// <returns></returns>
  335. public bool GetListenAddress(out string ip, out ushort port) => _server.GetListenAddress(out ip, out port);
  336. #endregion 查询服务器信息
  337. #region 查询客户端信息
  338. /// <summary>
  339. /// 获取所有连接
  340. /// </summary>
  341. /// <returns></returns>
  342. public List<IntPtr> GetAllConnectionIds() => _server.GetAllConnectionIds();
  343. /// <summary>
  344. /// 获取某客户端连接是否有效
  345. /// </summary>
  346. /// <param name="connId"></param>
  347. /// <returns></returns>
  348. public bool IsConnected(IntPtr connId) => _server.IsConnected(connId);
  349. /// <summary>
  350. /// 获取某客户端连接的接收状态
  351. /// </summary>
  352. /// <param name="connId"></param>
  353. /// <returns></returns>
  354. public ReceiveState GetReceiveState(IntPtr connId) => _server.GetReceiveState(connId);
  355. /// <summary>
  356. /// 获取某个连接的本地地址信息
  357. /// </summary>
  358. /// <param name="connId"></param>
  359. /// <param name="ip"></param>
  360. /// <param name="port"></param>
  361. /// <returns></returns>
  362. public bool GetLocalAddress(IntPtr connId, out string ip, out ushort port) => _server.GetLocalAddress(connId, out ip, out port);
  363. /// <summary>
  364. /// 获取某个连接的远程地址信息
  365. /// </summary>
  366. /// <param name="connId"></param>
  367. /// <param name="ip"></param>
  368. /// <param name="port"></param>
  369. /// <returns></returns>
  370. public bool GetRemoteAddress(IntPtr connId, out string ip, out ushort port) => _server.GetRemoteAddress(connId, out ip, out port);
  371. /// <summary>
  372. /// 获取指定连接的连接时长(毫秒)
  373. /// </summary>
  374. /// <param name="connId"></param>
  375. /// <param name="period"></param>
  376. /// <returns></returns>
  377. public bool GetConnectPeriod(IntPtr connId, out uint period) => _server.GetConnectPeriod(connId, out period);
  378. /// <summary>
  379. /// 获取某个连接静默时间(毫秒)
  380. /// </summary>
  381. /// <param name="connId"></param>
  382. /// <param name="period"></param>
  383. /// <returns></returns>
  384. public bool GetSilencePeriod(IntPtr connId, out uint period) => _server.GetSilencePeriod(connId, out period);
  385. /// <summary>
  386. /// 获取连接中未发出数据的长度
  387. /// </summary>
  388. /// <param name="connId"></param>
  389. /// <param name="length"></param>
  390. /// <returns></returns>
  391. public bool GetPendingDataLength(IntPtr connId, out int length) => _server.GetPendingDataLength(connId, out length);
  392. #endregion 查询客户端信息
  393. #region 断开与客户端的连接
  394. /// <summary>
  395. /// 断开与某个客户的连接
  396. /// </summary>
  397. /// <param name="connId"></param>
  398. /// <param name="force">是否强制断开</param>
  399. /// <returns></returns>
  400. public bool Disconnect(IntPtr connId, bool force = true) => _server.Disconnect(connId, force);
  401. /// <summary>
  402. /// 断开超过指定时间的连接
  403. /// </summary>
  404. /// <param name="period">毫秒</param>
  405. /// <param name="force">强制</param>
  406. /// <returns></returns>
  407. public bool DisconnectLongConnections(uint period, bool force = true) => _server.DisconnectLongConnections(period, force);
  408. /// <summary>
  409. /// 断开超过指定时长的静默连接
  410. /// </summary>
  411. /// <param name="period">毫秒</param>
  412. /// <param name="force">强制</param>
  413. /// <returns></returns>
  414. public bool DisconnectSilenceConnections(uint period, bool force = true) => _server.DisconnectSilenceConnections(period, force);
  415. #endregion 断开与客户端的连接
  416. #region 暂停/唤醒接收某客户端
  417. /// <summary>
  418. /// 暂停接收
  419. /// </summary>
  420. /// <param name="connId"></param>
  421. /// <returns></returns>
  422. public bool PauseReceive(IntPtr connId) => _server.PauseReceive(connId);
  423. /// <summary>
  424. /// 唤醒接收
  425. /// </summary>
  426. /// <param name="connId"></param>
  427. /// <returns></returns>
  428. public bool ResumeReceive(IntPtr connId) => _server.ResumeReceive(connId);
  429. #endregion 暂停/唤醒接收某客户端
  430. #endregion 常用方法
  431. #region 创建侦听的方法与示例方法(后使用Start()开启侦听)
  432. /// <summary>
  433. /// 创建侦听
  434. /// </summary>
  435. /// <param name="localAddr">地址</param>
  436. /// <param name="port">端口</param>
  437. /// <returns></returns>
  438. public ITcpServer CreateTcpServer()
  439. {
  440. _server = new TcpServer();
  441. return _server;
  442. }
  443. /// <summary>
  444. /// 创建TCP侦听 -示例
  445. /// </summary>
  446. /// <param name="localAddr">地址</param>
  447. /// <param name="port">端口</param>
  448. /// <returns></returns>
  449. public ITcpServer CreateTcpServerDemo(IPAddress localAddr, int port)
  450. {
  451. // <1> 创建服务器对象
  452. _server = new TcpServer();
  453. // <2> 设置socket接收长度
  454. _server.SocketBufferSize = 4096; // 4K
  455. _server.Address = "192.168.10.11";
  456. _server.Port = 8085;
  457. // <3> 绑定事件
  458. //event ServerAcceptEventHandler OnAccept; // TCP连接准备事件
  459. //event ServerHandShakeEventHandler OnHandShake; // TCP握手成功事件
  460. //event ServerPrepareListenEventHandler OnPrepareListen; // 监听事件
  461. //event ServerSendEventHandler OnSend; // 数据包发送事件
  462. //event ServerReceiveEventHandler OnReceive; // 数据包到达事件
  463. //event ServerCloseEventHandler OnClose; // TCP连接关闭事件
  464. //event ServerShutdownEventHandler OnShutdown; // TCP服务器关闭事件
  465. _server.OnAccept += OnAccept; // TCP连接准备事件-使用附加数据处理黏包
  466. _server.OnHandShake += OnHandShake; // TCP握手成功事件
  467. _server.OnPrepareListen += OnPrepareListen; // 监听事件
  468. _server.OnSend += OnSend; // 数据包发送事件
  469. _server.OnReceive += OnReceive; // 数据包接收事件
  470. _server.OnClose += OnClose; // TCP连接关闭事件
  471. _server.OnShutdown += OnShutdown; // TCP服务器关闭事件
  472. return _server;
  473. }
  474. #region TCP事件
  475. /// <summary>
  476. /// TCP连接事件(连接前)-使用附加数据处理黏包,不可异步
  477. /// </summary>
  478. /// <param name="sender">服务器对象</param>
  479. /// <param name="connId">连接ID</param>
  480. /// <param name="client">如果为 TCP 连接,pClient为 SOCKET 句柄;如果为 UDP 连接,pClient为 SOCKADDR 指针;</param>
  481. /// <returns></returns>
  482. private HandleResult OnAccept(IServer sender, IntPtr connId, IntPtr client)
  483. {
  484. // 获取客户端地址
  485. string ip = string.Empty;
  486. ushort port = 0;
  487. if (!sender.GetRemoteAddress(connId, out ip, out port))
  488. {
  489. return HandleResult.Error;
  490. }
  491. // 设置附加数据(用来做粘包处理)
  492. sender.SetExtra(connId, string.Empty); // 初始化附加信息
  493. //ShowLog(string.Format("TCP客户端接入({0}), ip: {1}, port: {2}", connId, ip, port));
  494. return HandleResult.Ok;
  495. }
  496. /// <summary>
  497. /// TCP握手成功事件
  498. /// </summary>
  499. /// <param name="sender">服务器对象</param>
  500. /// <param name="connId">连接ID</param>
  501. /// <returns></returns>
  502. private HandleResult OnHandShake(IServer sender, IntPtr connId)
  503. {
  504. // 一般用不到
  505. return HandleResult.Ok;
  506. }
  507. /// <summary>
  508. /// 监听事件
  509. /// </summary>
  510. /// <param name="server">服务器对象</param>
  511. /// <param name="intPtr">连接ID</param>
  512. /// <returns></returns>
  513. private HandleResult OnPrepareListen(IServer server, IntPtr intPtr)
  514. {
  515. //ShowLog(string.Format("TCP服务器开启监听({0}:{1}), listen:{2}", server.Address, server.Port, intPtr));
  516. return HandleResult.Ok;
  517. }
  518. /// <summary>
  519. /// 数据包发送事件
  520. /// </summary>
  521. /// <param name="sender">服务器对象</param>
  522. /// <param name="connId">连接ID</param>
  523. /// <param name="data">数据包</param>
  524. /// <returns></returns>
  525. private HandleResult OnSend(IServer sender, IntPtr connId, byte[] data)
  526. {
  527. string ip = string.Empty;
  528. ushort port = 0;
  529. sender.GetRemoteAddress(connId, out ip, out port); // 获取客户端地址
  530. //ShowLog(string.Format("TCP服务器发送数据[ID:{0},客户端地址:‘{1}:{2}’];数据[长度{3}]:{4}", connId, ip ?? "未找到IP", port, data.Length, Encoding.ASCII.GetString(data)));
  531. return HandleResult.Ok;
  532. }
  533. /// <summary>
  534. /// 数据包接收事件
  535. /// </summary>
  536. /// <param name="sender">服务器对象</param>
  537. /// <param name="connId">连接ID</param>
  538. /// <param name="data">数据包</param>
  539. /// <returns></returns>
  540. private HandleResult OnReceive(IServer sender, IntPtr connId, byte[] data)
  541. {
  542. // <1> 获取客户端地址
  543. string ip = string.Empty;
  544. ushort port = 0;
  545. if (!sender.GetRemoteAddress(connId, out ip, out port))
  546. {
  547. return HandleResult.Error;
  548. }
  549. // <2> 获取附加数据对象
  550. var extraDataStr = sender.GetExtra<string>(connId);
  551. if (extraDataStr == null)
  552. {
  553. return HandleResult.Error;
  554. }
  555. // <3> 将接收数据转换成字符串
  556. string msg = Encoding.ASCII.GetString(data);
  557. extraDataStr += msg; // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)
  558. // <4> 显示信息
  559. //ShowLog(string.Format("TCP服务器接收客户端[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", connId, ip, port, data.Length, msg));
  560. // <5> 处理数据
  561. HandleResult result;
  562. string _endsWith = "\r\n"; // TCP包的数据结束符
  563. while (true)
  564. {
  565. int index = extraDataStr.IndexOf(_endsWith);
  566. if (index == -1) // 数据接收不完整,忽略后等待下一次接收
  567. {
  568. result = HandleResult.Ignore;
  569. break;
  570. }
  571. else
  572. {
  573. string validMsg = extraDataStr.Remove(0, index);
  574. // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)
  575. // <7> 移除已取出数据
  576. extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
  577. }
  578. }
  579. // <8> 保存PacketData数据
  580. if (extraDataStr.Length > _server.SocketBufferSize) // 可选-控制长度为4096(注:实际所占控件大小>4096)
  581. {
  582. int length_Delete = extraDataStr.Length - Convert.ToInt32(_server.SocketBufferSize);
  583. extraDataStr = extraDataStr.Remove(0, length_Delete); // 清除长度超标数据
  584. }
  585. sender.SetExtra(connId, extraDataStr); // 初始化附加信息
  586. return result;
  587. }
  588. /// <summary>
  589. /// TCP连接关闭事件
  590. /// </summary>
  591. /// <param name="sender">服务器对象</param>
  592. /// <param name="connId">连接ID</param>
  593. /// <param name="socketOperation">关闭的类型</param>
  594. /// <param name="errorCode">错误时的错误代码</param>
  595. /// <returns></returns>
  596. private HandleResult OnClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
  597. {
  598. // <1> 获取客户端地址
  599. string ip = string.Empty;
  600. ushort port = 0;
  601. if (!sender.GetRemoteAddress(connId, out ip, out port))
  602. {
  603. return HandleResult.Ignore;
  604. }
  605. // <2> 释放附加数据
  606. if (sender.GetExtra<string>(connId) != null)
  607. {
  608. sender.RemoveExtra(connId); // 删除附加数据
  609. }
  610. // <3> 显示信息
  611. //ShowLog(string.Format("TCP客户端连接断开(ID {0},地址 {1}:{2}), 断开类型: {3},错误代码: {4}", connId, ip, port, socketOperation.ToString(), errorCode));
  612. return HandleResult.Ok;
  613. }
  614. /// <summary>
  615. /// TCP服务器关闭事件
  616. /// </summary>
  617. /// <param name="sender">服务器对象</param>
  618. /// <returns></returns>
  619. private HandleResult OnShutdown(IServer sender)
  620. {
  621. //ShowLog(string.Format("TCP服务器关闭({0}:{1})", sender.Address, sender.Port));
  622. return HandleResult.Ok;
  623. }
  624. #endregion TCP事件
  625. #endregion 创建侦听的方法与示例方法(后使用Start()开启侦听)
  626. #endregion 接收端(服务端)
  627. }
  628. /// <summary>
  629. /// HPSocket_TcpClient示例帮助类(示例学习)
  630. /// System.Net.Sockets类库
  631. /// TcpClient
  632. /// </summary>
  633. public class HPSocket_TcpClientHelper
  634. {
  635. #region 发送端(客户端)
  636. /// <summary>
  637. /// TCP客户端
  638. /// </summary>
  639. public ITcpClient _client = new TcpClient();
  640. /// <summary>
  641. /// 本地绑定到哪个ip
  642. /// </summary>
  643. public string BindAddress
  644. {
  645. get { return _client.BindAddress; }
  646. set { _client.BindAddress = value; }
  647. }
  648. /// <summary>
  649. /// 本地绑定到哪个端口
  650. /// </summary>
  651. public ushort BindPort
  652. {
  653. get { return _client.BindPort; }
  654. set { _client.BindPort = value; }
  655. }
  656. /// <summary>
  657. /// 远程服务器地址
  658. /// </summary>
  659. public string Address
  660. {
  661. get { return _client.Address; }
  662. set { _client.Address = value; }
  663. }
  664. /// <summary>
  665. /// 远程服务器端口
  666. /// </summary>
  667. public ushort Port
  668. {
  669. get { return _client.Port; }
  670. set { _client.Port = value; }
  671. }
  672. #region 创建时的设置项
  673. /// <summary>
  674. /// 是否异步连接,默认为真
  675. /// </summary>
  676. public bool Async
  677. {
  678. get { return _client.Async; }
  679. set { _client.Async = value; }
  680. }
  681. /// <summary>
  682. /// 读取或设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为:(N * 1024) - sizeof(TBufferObj))
  683. /// </summary>
  684. public uint SocketBufferSize
  685. {
  686. get { return _client.SocketBufferSize; }
  687. set { _client.SocketBufferSize = value; }
  688. }
  689. /// <summary>
  690. /// 连接超时时间, 默认操作系统默认值
  691. /// 单位: 毫秒
  692. /// 异常:T:System.InvalidOperationException:同步连接、.NET Framework2.0以及设置小于100毫秒会引发此异常
  693. /// </summary>
  694. public int ConnectionTimeout
  695. {
  696. get { return _client.ConnectionTimeout; }
  697. set { _client.ConnectionTimeout = value; }
  698. }
  699. /// <summary>
  700. /// 附加数据
  701. /// 赋值:client.ExtraData = myObj;
  702. /// 取值:var data = ExtraData as MyData;
  703. /// </summary>
  704. public object ExtraData
  705. {
  706. get { return _client.ExtraData; }
  707. set { _client.ExtraData = value; }
  708. }
  709. /// <summary>
  710. /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
  711. /// </summary>
  712. public bool NoDelay
  713. {
  714. get { return _client.NoDelay; }
  715. set { _client.NoDelay = value; }
  716. }
  717. #region 心跳
  718. /// <summary>
  719. /// 读取或设置心跳包间隔(毫秒,0 则不发送心跳包)
  720. /// </summary>
  721. public uint KeepAliveTime
  722. {
  723. get { return _client.KeepAliveTime; }
  724. set { _client.KeepAliveTime = value; }
  725. }
  726. /// <summary>
  727. /// 读取或设置心跳确认包检测间隔(毫秒,0 不发送心跳包,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线)
  728. /// </summary>
  729. public uint KeepAliveInterval
  730. {
  731. get { return _client.KeepAliveInterval; }
  732. set { _client.KeepAliveInterval = value; }
  733. }
  734. #endregion 心跳
  735. /// <summary>
  736. /// 获取或设置暂停接收状态,设置状态时,不允许设置为ReceiveState.Unknown,
  737. /// </summary>
  738. public ReceiveState PauseReceive
  739. {
  740. get { return _client.PauseReceive; }
  741. set { _client.PauseReceive = value; }
  742. }
  743. /// <summary>
  744. /// 获取或设置地址重用选项
  745. /// </summary>
  746. public ReuseAddressPolicy ReuseAddressPolicy
  747. {
  748. get { return _client.ReuseAddressPolicy; }
  749. set { _client.ReuseAddressPolicy = value; }
  750. }
  751. /// <summary>
  752. /// 代理列表
  753. /// </summary>
  754. public List<IProxy> ProxyList
  755. {
  756. get { return _client.ProxyList; }
  757. set { _client.ProxyList = value; }
  758. }
  759. #region 性能优化
  760. /// <summary>
  761. /// 读取或设置内存块缓存池大小(通常设置为 -> PUSH 模型:5 - 10;PULL 模型:10 - 20 )
  762. /// </summary>
  763. public uint FreeBufferPoolSize
  764. {
  765. get { return _client.FreeBufferPoolSize; }
  766. set { _client.FreeBufferPoolSize = value; }
  767. }
  768. /// <summary>
  769. /// 读取或设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍)
  770. /// </summary>
  771. public uint FreeBufferPoolHold
  772. {
  773. get { return _client.FreeBufferPoolHold; }
  774. set { _client.FreeBufferPoolHold = value; }
  775. }
  776. #endregion 性能优化
  777. #endregion 创建时的设置项
  778. #region 常用属性
  779. /// <summary>
  780. /// 检查通信组件是否已启动
  781. /// </summary>
  782. public bool HasStarted => _client.HasStarted;
  783. /// <summary>
  784. /// 状态
  785. /// </summary>
  786. public ServiceState State => _client.State;
  787. /// <summary>
  788. /// 是否已连接
  789. /// </summary>
  790. public bool IsConnected => _client.IsConnected;
  791. /// <summary>
  792. /// 是否为安全连接(SSL/HTTPS)
  793. /// </summary>
  794. public bool IsSecure => _client.IsSecure;
  795. /// <summary>
  796. /// 获取该组件对象的连接Id
  797. /// </summary>
  798. public IntPtr ConnectionId => _client.ConnectionId;
  799. #endregion 常用属性
  800. #region 常用方法
  801. #region 启动并连接到服务器
  802. /// <summary>
  803. /// 启动通讯组件并连接到服务器
  804. /// </summary>
  805. /// <returns></returns>
  806. public bool Connect() => _client.Connect();
  807. /// <summary>
  808. /// 启动通讯组件并连接到服务器
  809. /// </summary>
  810. /// <param name="address">远程服务器地址</param>
  811. /// <param name="port">远程服务器端口</param>
  812. /// <returns></returns>
  813. public bool Connect(string address, ushort port) => _client.Connect(address, port);
  814. #endregion 启动并连接到服务器
  815. /// <summary>
  816. /// 停止服务
  817. /// </summary>
  818. /// <returns></returns>
  819. public bool Stop() => _client.Stop();
  820. #region 发送数据
  821. /// <summary>
  822. /// 发送数据
  823. /// </summary>
  824. /// <param name="bytes">数据包</param>
  825. /// <param name="length">数据包长度</param>
  826. /// <returns></returns>
  827. public bool Send(byte[] bytes, int length) => _client.Send(bytes, length);
  828. /// <summary>
  829. /// 发送数据
  830. /// </summary>
  831. /// <param name="bytes">数据包</param>
  832. /// <param name="offset">针对bytes的偏移</param>
  833. /// <param name="length">数据包长度</param>
  834. /// <returns></returns>
  835. public bool Send(byte[] bytes, int offset, int length) => _client.Send(bytes, offset, length);
  836. /// <summary>
  837. /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
  838. /// </summary>
  839. /// <param name="buffers">发送缓冲区数组</param>
  840. /// <param name="count">发送缓冲区数目</param>
  841. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  842. public bool SendPackets(Wsabuf[] buffers, int count) => _client.SendPackets(buffers, count);
  843. #endregion 发送数据
  844. #region 发送本地小文件
  845. /// <summary>
  846. /// 发送本地小文件
  847. // 向指定连接发送 4096 KB 以下的小文件
  848. /// </summary>
  849. /// <param name="filePath">文件路径</param>
  850. /// <param name="head">头部附加数据</param>
  851. /// <param name="tail">尾部附加数据</param>
  852. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  853. public bool SendSmallFile(string filePath, ref Wsabuf head, ref Wsabuf tail) => _client.SendSmallFile(filePath, ref head, ref tail);
  854. /// <summary>
  855. /// 发送本地小文件
  856. /// 向指定连接发送 4096 KB 以下的小文件
  857. /// </summary>
  858. /// <param name="filePath">文件路径</param>
  859. /// <param name="head">头部附加数据,可以为null</param>
  860. /// <param name="tail">尾部附加数据,可以为null</param>
  861. /// <returns>true.成功,false.失败,可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
  862. public bool SendSmallFile(string filePath, byte[] head, byte[] tail) => _client.SendSmallFile(filePath, head, tail);
  863. #endregion 发送本地小文件
  864. #region 查询连接信息
  865. /// <summary>
  866. /// 获取监听socket的地址信息
  867. /// </summary>
  868. /// <param name="host"></param>
  869. /// <param name="port"></param>
  870. /// <returns></returns>
  871. public bool GetListenAddress(out string host, out ushort port) => _client.GetListenAddress(out host, out port);
  872. /// <summary>
  873. /// 获取连接的远程主机信息
  874. /// </summary>
  875. /// <param name="host"></param>
  876. /// <param name="port"></param>
  877. /// <returns></returns>
  878. public bool GetRemoteHost(out string host, out ushort port) => _client.GetRemoteHost(out host, out port);
  879. /// <summary>
  880. /// 获取连接中未发出数据的长度
  881. /// </summary>
  882. /// <param name="length"></param>
  883. /// <returns></returns>
  884. public bool GetPendingDataLength(out int length) => _client.GetPendingDataLength(out length);
  885. #endregion 查询连接信息
  886. /// <summary>
  887. /// 等待代理结果
  888. /// </summary>
  889. /// <returns>连接成功返回true, 连接失败返回false</returns>
  890. //public async Task<bool> WaitProxyAsync() => await _client.WaitProxyAsync();
  891. #endregion 常用方法
  892. #region 创建TCPClient并连接示例
  893. /// <summary>
  894. /// 创建TCP侦听 -示例
  895. /// </summary>
  896. /// <param name="localAddr">地址</param>
  897. /// <param name="port">端口</param>
  898. /// <returns></returns>
  899. public ITcpClient CreateTcpServerDemo(string localAddr, int port)
  900. {
  901. // <2>配置TCPServer的参数
  902. _client.Address = localAddr; // IP
  903. _client.Port = (ushort)port; // 端口
  904. _client.SocketBufferSize = 4096; // 缓存4K
  905. //_client.BindAddress = ""; // 本地绑定到哪个ip
  906. //_client.BindPort = 0; // 本地绑定到哪个端口
  907. // <3> 绑定事件
  908. //event ClientPrepareConnectEventHandler OnPrepareConnect; // 准备连接了事件
  909. //event ClientConnectEventHandler OnConnect; // 连接事件
  910. //event ClientHandShakeEventHandler OnHandShake; // TCP握手成功事件
  911. //event ClientSendEventHandler OnSend; // 数据包发送事件
  912. //event ClientReceiveEventHandler OnReceive; // 数据包到达事件
  913. //event ClientCloseEventHandler OnClose; // TCP连接关闭事件
  914. _client.OnPrepareConnect += OnPrepareConnect; // 准备连接了事件
  915. _client.OnConnect += OnConnect; // 连接事件
  916. _client.OnHandShake += OnHandShake; // TCP握手成功事件
  917. _client.OnSend += OnSend; // 数据包发送事件
  918. _client.OnReceive += OnReceive; // 数据包到达事件
  919. _client.OnClose += OnClose; // TCP连接关闭事件
  920. //_client.Connect(); // 连接
  921. return _client;
  922. }
  923. #region 事件
  924. /// <summary>
  925. /// 准备连接了事件
  926. /// </summary>
  927. /// <param name="sender">客户端</param>
  928. /// <param name="socket">客户端Id</param>
  929. /// <returns></returns>
  930. private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
  931. {
  932. sender.ExtraData = string.Empty; // 设置附加数据(用来做粘包处理)
  933. return HandleResult.Ok;
  934. }
  935. /// <summary>
  936. /// 连接事件
  937. /// </summary>
  938. /// <param name="sender">客户端</param>
  939. /// <returns></returns>
  940. private HandleResult OnConnect(IClient sender)
  941. {
  942. //ShowLog(string.Format("TCP客户端([{0}]{1}:{2})接入TCP服务器{3}:{4}", sender.ConnectionId, sender.BindAddress, sender.BindPort, sender.Address, sender.Port));
  943. return HandleResult.Ok;
  944. }
  945. /// <summary>
  946. /// TCP握手成功事件
  947. /// </summary>
  948. /// <param name="sender">客户端</param>
  949. /// <returns></returns>
  950. private HandleResult OnHandShake(IClient sender)
  951. {
  952. // 一般用不到
  953. return HandleResult.Ok;
  954. }
  955. /// <summary>
  956. /// 数据包发送事件
  957. /// </summary>
  958. /// <param name="sender">客户端</param>
  959. /// <param name="data">数据</param>
  960. /// <returns></returns>
  961. private HandleResult OnSend(IClient sender, byte[] data)
  962. {
  963. //ShowLog(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]:{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
  964. return HandleResult.Ok;
  965. }
  966. /// <summary>
  967. /// 数据包到达事件
  968. /// </summary>
  969. /// <param name="sender">客户端</param>
  970. /// <param name="data">数据</param>
  971. /// <returns></returns>
  972. private HandleResult OnReceive(IClient sender, byte[] data)
  973. {
  974. // <1> 获取附加数据对象
  975. if (!(sender.ExtraData is string))
  976. {
  977. return HandleResult.Error;
  978. }
  979. string extraDataStr = (string)sender.ExtraData;
  980. // <2> 将接收数据转换成字符串
  981. string msg = Encoding.UTF8.GetString(data);
  982. extraDataStr += msg; // 添加数据到缓存_不合格的数据添加到缓存区(用于粘包、拆包)
  983. // <3> 显示信息
  984. //ShowLog(string.Format("TCP客户端接收到TCP服务器[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, msg));
  985. // <4> 处理数据
  986. HandleResult result;
  987. string _endsWith = "\r\n";
  988. while (true)
  989. {
  990. int index = extraDataStr.IndexOf(_endsWith);
  991. if (index == -1) // 数据接收不完整,忽略后等待下一次接收
  992. {
  993. result = HandleResult.Ignore;
  994. break;
  995. }
  996. else
  997. {
  998. string validMsg = extraDataStr.Remove(0, index);
  999. // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)
  1000. // <7> 移除已取出数据
  1001. extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
  1002. }
  1003. }
  1004. // <5> 保存PacketData数据
  1005. if (extraDataStr.Length > _client.SocketBufferSize) // 可选-控制长度为4096(注:实际所占空间大小>4096)
  1006. {
  1007. int length_Delete = extraDataStr.Length - Convert.ToInt32(_client.SocketBufferSize);
  1008. extraDataStr = extraDataStr.Remove(0, length_Delete); // 清除长度超标数据
  1009. }
  1010. sender.ExtraData = extraDataStr; // 初始化附加信息
  1011. return result;
  1012. }
  1013. /// <summary>
  1014. /// TCP连接关闭事件
  1015. /// </summary>
  1016. /// <param name="sender">客户端</param>
  1017. /// <param name="socketOperation">关闭类型</param>
  1018. /// <param name="errorCode">错误代码</param>
  1019. /// <returns></returns>
  1020. private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
  1021. {
  1022. sender.ExtraData = null; // 删除附加数据
  1023. //ShowLog(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
  1024. return HandleResult.Ok;
  1025. }
  1026. #endregion 事件
  1027. #endregion 创建TCPClient并连接示例
  1028. #endregion 发送端(客户端)
  1029. }
  1030. }