LeadSocketServerHelper.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using NX_CommonClassLibrary;
  2. using NX_LogClassLibrary;
  3. using NX_ModelClassLibrary.BaseModel;
  4. using NX_ModelClassLibrary.CustomEnum;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Text;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. namespace NX_SocketUtility
  15. {
  16. /// <summary>
  17. /// 先导智能Socket服务端操作帮助类
  18. /// Copyright20181102 (C) sunyalong
  19. /// 允许修改、添加满足自己项目的需要。
  20. /// 添加、修改后请详细注释。违者会强制删除不予采用。
  21. /// </summary>
  22. public class LeadSocketServerHelper
  23. {
  24. #region 事件
  25. public event GetMessageEventHandler MessageEvent;
  26. /// <summary>
  27. /// 触发接收到Socket客户端消息的委托事件
  28. /// </summary>
  29. /// <param name="sender">数据buffer</param>
  30. /// <param name="ip">ip</param>
  31. /// <param name="dataLength">数据长度</param>
  32. /// <param name="e">事件类对象</param>
  33. protected virtual void OnNewMessage(object sender, string ip, int dataLength, EventArgs e)
  34. {
  35. MessageEvent?.Invoke(sender, ip, dataLength, e);
  36. }
  37. public event GetAcceptEventHandler AcceptEvent;
  38. /// <summary>
  39. /// 触发Socket客户接入服务端的委托事件
  40. /// </summary>
  41. /// <param name="sender">链接进入的IP(Socket对象)</param>
  42. /// <param name="e">事件类对象</param>
  43. protected virtual void OnAcceptEvent(object sender, EventArgs e)
  44. {
  45. AcceptEvent?.Invoke(sender, e);
  46. }
  47. public event GetCloseEventHandler CloseEvent;
  48. /// <summary>
  49. /// 触发关闭链接委托事件
  50. /// </summary>
  51. /// <param name="sender">链接进入的IP(Socket对象)</param>
  52. /// <param name="e">事件类对象</param>
  53. protected virtual void OnCloseEvent(object sender, EventArgs e)
  54. {
  55. CloseEvent?.Invoke(sender, e);
  56. }
  57. #endregion
  58. #region 全局变量
  59. /// <summary>
  60. /// Socket服务端套接字对象
  61. /// </summary>
  62. public Socket SocketServerListen;
  63. /// <summary>
  64. /// Socket客户端套接字对象字典
  65. /// </summary>
  66. public ConcurrentDictionary<string, Socket> dicSockClientList = new ConcurrentDictionary<string, Socket>();
  67. /// <summary>
  68. /// Socket客户端接受数据线程字典
  69. /// </summary>
  70. public ConcurrentDictionary<string, Thread> dicSockThreadList = new ConcurrentDictionary<string, Thread>();
  71. /// <summary>
  72. /// 监听客户端连接的线程标识
  73. /// </summary>
  74. private bool Flag_Listen = false;
  75. #endregion
  76. #region 方法
  77. /// <summary>
  78. /// 开启监听
  79. /// </summary>
  80. /// <param name="ip">服务端IP地址</param>
  81. /// <param name="port"服务端>端口号</param>
  82. public void StartListen(string ip, int port)
  83. {
  84. try
  85. {
  86. //当开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
  87. SocketServerListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  88. //获取ip地址
  89. IPAddress ipadd = IPAddress.Parse(ip);
  90. //创建一个网络通信节点,这个通信节点包含了ip地址,端口号。
  91. //这里的端口我们设置为1029,这里设置大于1024,为什么自己查一下端口号范围使用说明。
  92. IPEndPoint endpoint = new IPEndPoint(ipadd, Convert.ToInt32(port));//创建一个网络通信节点,该节点中包含了IP地址和端口号.
  93. //Socket绑定网络通信节点
  94. SocketServerListen.Bind(endpoint);
  95. //设置监听队列
  96. SocketServerListen.Listen(100);
  97. LogHelper.WriteLog($"Socket服务端 ip地址:【{ip}】端口号:【{port}】 开启监听成功!", LogTypeEnum.SocketRun);
  98. Flag_Listen = true;
  99. }
  100. catch (Exception ex)
  101. {
  102. LogHelper.WriteLog($"Socket服务端 ip地址:【{ip}】端口号:【{port}】 开启监听发生异常:【{ex.Message}】", LogTypeEnum.SocketErr);
  103. }
  104. }
  105. #region UDP协议
  106. public void StartListenUdp(string ip, int port, List<BasBarcodeScannerMd> allBarcodeScannerList)
  107. {
  108. try
  109. {
  110. //当开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
  111. SocketServerListen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  112. //获取ip地址
  113. IPAddress ipadd = IPAddress.Parse(ip);
  114. //创建一个网络通信节点,这个通信节点包含了ip地址,端口号。
  115. //这里的端口我们设置为1029,这里设置大于1024,为什么自己查一下端口号范围使用说明。
  116. IPEndPoint endpoint = new IPEndPoint(ipadd, Convert.ToInt32(port));//创建一个网络通信节点,该节点中包含了IP地址和端口号.
  117. //Socket绑定网络通信节点
  118. SocketServerListen.Bind(endpoint);
  119. LogHelper.WriteLog($"Socket服务端 ip地址:【{ip}】端口号:【{port}】 开启监听成功!", LogTypeEnum.SocketRun);
  120. ParameterizedThreadStart par = new ParameterizedThreadStart(ReciveMsg);
  121. Thread t = new Thread(par)
  122. {
  123. IsBackground = true
  124. };
  125. t.Start(allBarcodeScannerList);
  126. }
  127. catch (Exception ex)
  128. {
  129. LogHelper.WriteLog($"Socket服务端 ip地址:【{ip}】端口号:【{port}】 开启监听发生异常:【{ex.Message}】", LogTypeEnum.SocketErr);
  130. }
  131. }
  132. void ReciveMsg(object allBarcodeScannerList)
  133. {
  134. List<BasBarcodeScannerMd> scannerList = allBarcodeScannerList as List<BasBarcodeScannerMd>;
  135. while (true)
  136. {
  137. try
  138. {
  139. EndPoint point = new IPEndPoint(IPAddress.Any, 0);
  140. byte[] buffer = new byte[1024 * 1024];
  141. int length = SocketServerListen.ReceiveFrom(buffer, ref point);
  142. if (scannerList.FindAll(x => x.IpAddress == ((IPEndPoint)point).Address.ToString()).Count > 0)
  143. {
  144. //string message = Encoding.UTF8.GetString(buffer, 0, length);
  145. //触发接收到Socket客户端发送数据的事件
  146. OnNewMessage(buffer, point.ToString(), length, EventArgs.Empty);
  147. }
  148. }
  149. catch
  150. {
  151. }
  152. }
  153. }
  154. #endregion
  155. /// <summary>
  156. /// 循环监听客户端的连接
  157. /// </summary>
  158. public void ListeneSocketClientConnectAccept()
  159. {
  160. while (Flag_Listen)
  161. {
  162. try
  163. {
  164. Socket newSoket = SocketServerListen.Accept();
  165. string ipAddress = newSoket.RemoteEndPoint.ToString();
  166. ParameterizedThreadStart par = new ParameterizedThreadStart(RecevieMsg);
  167. Thread thread = new Thread(par)
  168. {
  169. IsBackground = true
  170. };
  171. thread.Start(newSoket);
  172. dicSockClientList.TryAdd(ipAddress, newSoket);
  173. dicSockThreadList.TryAdd(ipAddress,thread);
  174. //触发Socket客户接入服务端事件
  175. OnAcceptEvent(ipAddress, EventArgs.Empty);
  176. LogHelper.WriteLog($"Socket客户端:【{ipAddress}】连接成功!", LogTypeEnum.SocketRun);
  177. }
  178. catch (Exception ex)
  179. {
  180. LogHelper.WriteLog($"Socket服务端监听客户端的连接的函数发生异常:【{ex.Message}】", LogTypeEnum.SocketErr);
  181. }
  182. }
  183. }
  184. /// <summary>
  185. /// //该方法负责接收从客户端发送过来的数据
  186. /// </summary>
  187. /// <param name="socket">Socket套接字</param>
  188. public void RecevieMsg(object socket)
  189. {
  190. Socket newSocket = socket as Socket;//转成对应的Socket类型
  191. string IPPortID = newSocket.RemoteEndPoint.ToString();//这个线程ID
  192. while (Flag_Listen)
  193. {
  194. byte[] buffer = new byte[1024 * 2];
  195. int receiveLength;
  196. try //由于Socket中的Receive方法容易抛出异常,所以我们在这里要捕获异常。
  197. {
  198. receiveLength = dicSockClientList[IPPortID].Receive(buffer);//接收从客户端发送过来的数据
  199. }
  200. catch (Exception ex)
  201. {
  202. dicSockClientList.TryRemove(IPPortID, out Socket tryRemoveSocket);
  203. if (tryRemoveSocket != null)
  204. {
  205. tryRemoveSocket.Disconnect(false);
  206. tryRemoveSocket.Close();
  207. }
  208. //dicSockThreadList[IPPortID].Abort();
  209. // dicSockThreadList.TryRemove(IPPortID, out _);
  210. //newSocket.Disconnect(false);
  211. newSocket.Close();
  212. LogHelper.WriteLog($"Socket客户端:【{IPPortID}】数据接受发生异常:【{ex.Message}】,Socket服务端已经移除该客户端连接!", LogTypeEnum.SocketErr);
  213. //触发Socket客户端断开连接实践
  214. OnCloseEvent(IPPortID, EventArgs.Empty);
  215. break;
  216. }
  217. if (receiveLength > 0)
  218. {
  219. //记录接收到的原始数据
  220. LogHelper.WriteLog($"Socket客户端:【{IPPortID}】接收到:【{receiveLength}】字节数据,数据内容:【{BitConverter.ToString(buffer, 0, receiveLength)}】。", LogTypeEnum.SocketData);
  221. }
  222. //触发接收到Socket客户端发送数据的事件
  223. OnNewMessage(buffer, IPPortID, receiveLength, EventArgs.Empty);
  224. }
  225. }
  226. /// <summary>
  227. /// 关闭服务
  228. /// </summary>
  229. public void CloseServer()
  230. {
  231. lock (dicSockClientList)
  232. {
  233. foreach (var item in dicSockClientList)
  234. {
  235. item.Value.Close();//关闭每一个连接
  236. }
  237. dicSockClientList.Clear();//清除字典
  238. }
  239. lock (dicSockThreadList)
  240. {
  241. foreach (var item in dicSockThreadList)
  242. {
  243. item.Value.Abort();//停止线程
  244. }
  245. dicSockThreadList.Clear();
  246. }
  247. Flag_Listen = false;
  248. //服务端不能主动关闭连接,需要把监听到的连接逐个关闭
  249. //SocketServerListen.Shutdown(SocketShutdown.Both);
  250. if (SocketServerListen != null)
  251. {
  252. SocketServerListen.Close();
  253. }
  254. }
  255. #endregion
  256. }
  257. #region 定义委托、事件
  258. /// <summary>
  259. /// 获取消息委托
  260. /// </summary>
  261. /// <param name="sender">消息主体buffer</param>
  262. /// <param name="ip">ip</param>
  263. /// <param name="dataLength">数据长度</param>
  264. /// <param name="e">事件类对象</param>
  265. public delegate void GetMessageEventHandler(object sender, string ip, int dataLength, EventArgs e);
  266. /// <summary>
  267. /// 获取链接委托
  268. /// </summary>
  269. /// <param name="sender">链接进入的IP</param>
  270. /// <param name="e">事件类对象</param>
  271. public delegate void GetAcceptEventHandler(object sender, EventArgs e);
  272. /// <summary>
  273. /// 关闭链接委托
  274. /// </summary>
  275. /// <param name="sender">链接进入的IP</param>
  276. /// <param name="e">事件类对象</param>
  277. public delegate void GetCloseEventHandler(object sender, EventArgs e);
  278. #endregion
  279. }