SocketClient.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Net.Sockets;
  6. using System.Net;
  7. using System.Threading;
  8. namespace AGV_WPF.Services
  9. {
  10. public class SocketClient
  11. {
  12. //委托
  13. private delegate void delSocketDataArrival(byte[] data);
  14. static delSocketDataArrival socketDataArrival = socketDataArrivalHandler;
  15. private delegate void delSocketDisconnected();
  16. static delSocketDisconnected socketDisconnected = socketDisconnectedHandler;
  17. public static Socket theSocket = null;
  18. private static string remoteHost = "127.0.0.1";
  19. private static int remotePort = 8059;
  20. private static String SockErrorStr = null;
  21. private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
  22. private static Boolean IsconnectSuccess = false; //异步连接情况,由异步连接回调函数置位
  23. private static object lockObj_IsConnectSuccess = new object();
  24. /// <summary>
  25. /// 构造函数
  26. /// </summary>
  27. /// <param name="strIp"></param>
  28. /// <param name="iPort"></param>
  29. public SocketClient(string strIp, int iPort)
  30. {
  31. remoteHost = strIp;
  32. remotePort = iPort;
  33. }
  34. /// <summary>
  35. /// 设置心跳
  36. /// </summary>
  37. private static void SetXinTiao()
  38. {
  39. //byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间20 秒, 间隔侦测时间2 秒
  40. byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间5 秒, 间隔侦测时间2 秒
  41. theSocket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
  42. }
  43. /// <summary>
  44. /// 创建套接字+异步连接函数
  45. /// </summary>
  46. /// <returns></returns>
  47. private static bool socket_create_connect()
  48. {
  49. IPAddress ipAddress = IPAddress.Parse(remoteHost);
  50. IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
  51. theSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  52. theSocket.SendTimeout = 1000;
  53. SetXinTiao();//设置心跳参数
  54. #region 异步连接代码
  55. TimeoutObject.Reset(); //复位timeout事件
  56. try
  57. {
  58. theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
  59. }
  60. catch (Exception err)
  61. {
  62. SockErrorStr = err.ToString();
  63. return false;
  64. }
  65. if (TimeoutObject.WaitOne(10000, false))//直到timeout,或者TimeoutObject.set()
  66. {
  67. if (IsconnectSuccess)
  68. {
  69. return true;
  70. }
  71. else
  72. {
  73. return false;
  74. }
  75. }
  76. else
  77. {
  78. SockErrorStr = "Time Out";
  79. return false;
  80. }
  81. #endregion
  82. }
  83. /// <summary>
  84. /// 同步receive函数
  85. /// </summary>
  86. /// <param name="readBuffer"></param>
  87. /// <returns></returns>
  88. public string socket_receive(byte[] readBuffer)
  89. {
  90. try
  91. {
  92. if (theSocket == null)
  93. {
  94. socket_create_connect();
  95. }
  96. else if (!theSocket.Connected)
  97. {
  98. if (!IsSocketConnected())
  99. Reconnect();
  100. }
  101. int bytesRec = theSocket.Receive(readBuffer);
  102. if (bytesRec == 0)
  103. {
  104. //warning 0 bytes received
  105. }
  106. return Encoding.ASCII.GetString(readBuffer, 0, bytesRec);
  107. }
  108. catch (SocketException se)
  109. {
  110. //print se.ErrorCode
  111. throw;
  112. }
  113. }
  114. /// <summary>
  115. /// 同步send函数
  116. /// </summary>
  117. /// <param name="sendMessage"></param>
  118. /// <returns></returns>
  119. public bool socket_send(byte[] buf)
  120. {
  121. if (checkSocketState())
  122. {
  123. return SendData(buf);
  124. }
  125. return false;
  126. }
  127. /// <summary>
  128. /// 断线重连函数
  129. /// </summary>
  130. /// <returns></returns>
  131. private static bool Reconnect()
  132. {
  133. //关闭socket
  134. theSocket.Shutdown(SocketShutdown.Both);
  135. theSocket.Disconnect(true);
  136. IsconnectSuccess = false;
  137. theSocket.Close();
  138. //创建socket
  139. return socket_create_connect();
  140. }
  141. /// <summary>
  142. /// 当socket.connected为false时,进一步确定下当前连接状态
  143. /// </summary>
  144. /// <returns></returns>
  145. private bool IsSocketConnected()
  146. {
  147. #region remarks
  148. /********************************************************************************************
  149. * 当Socket.Conneted为false时, 如果您需要确定连接的当前状态,请进行非阻塞、零字节的 Send 调用。
  150. * 如果该调用成功返回或引发 WAEWOULDBLOCK 错误代码 (10035),则该套接字仍然处于连接状态;
  151. * 否则,该套接字不再处于连接状态。
  152. * Depending on http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connected.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
  153. ********************************************************************************************/
  154. #endregion
  155. #region 过程
  156. // This is how you can determine whether a socket is still connected.
  157. bool connectState = true;
  158. bool blockingState = theSocket.Blocking;
  159. try
  160. {
  161. byte[] tmp = new byte[1];
  162. theSocket.Blocking = false;
  163. theSocket.Send(tmp, 0, 0);
  164. //Console.WriteLine("Connected!");
  165. connectState = true; //若Send错误会跳去执行catch体,而不会执行其try体里其之后的代码
  166. }
  167. catch (SocketException e)
  168. {
  169. // 10035 == WSAEWOULDBLOCK
  170. if (e.NativeErrorCode.Equals(10035))
  171. {
  172. //Console.WriteLine("Still Connected, but the Send would block");
  173. connectState = true;
  174. }
  175. else
  176. {
  177. //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
  178. connectState = false;
  179. }
  180. }
  181. finally
  182. {
  183. theSocket.Blocking = blockingState;
  184. }
  185. //Console.WriteLine("Connected: {0}", client.Connected);
  186. return connectState;
  187. #endregion
  188. }
  189. /// <summary>
  190. /// 另一种判断connected的方法,但未检测对端网线断开或ungraceful的情况
  191. /// </summary>
  192. /// <param name="s"></param>
  193. /// <returns></returns>
  194. public static bool IsSocketConnected(Socket s)
  195. {
  196. #region remarks
  197. /* As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into consideration
  198. * that the socket might not have been initialized in the first place.
  199. * This is the last (I believe) piece of information and it is supplied by the Socket.Connected property.
  200. * The revised version of the method would looks something like this:
  201. * from:http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c */
  202. #endregion
  203. #region 过程
  204. if (s == null)
  205. return false;
  206. return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
  207. /* The long, but simpler-to-understand version:
  208. bool part1 = s.Poll(1000, SelectMode.SelectRead);
  209. bool part2 = (s.Available == 0);
  210. if ((part1 && part2 ) || !s.Connected)
  211. return false;
  212. else
  213. return true;
  214. */
  215. #endregion
  216. }
  217. /// <summary>
  218. /// 异步连接回调函数
  219. /// </summary>
  220. /// <param name="iar"></param>
  221. static void connectedCallback(IAsyncResult iar)
  222. {
  223. #region <remarks>
  224. /// 1、置位IsconnectSuccess
  225. #endregion </remarks>
  226. lock (lockObj_IsConnectSuccess)
  227. {
  228. Socket client = (Socket)iar.AsyncState;
  229. try
  230. {
  231. client.EndConnect(iar);
  232. IsconnectSuccess = true;
  233. StartKeepAlive(); //开始KeppAlive检测
  234. }
  235. catch (Exception e)
  236. {
  237. //Console.WriteLine(e.ToString());
  238. SockErrorStr = e.ToString();
  239. IsconnectSuccess = false;
  240. }
  241. finally
  242. {
  243. TimeoutObject.Set();
  244. }
  245. }
  246. }
  247. /// <summary>
  248. /// 开始KeepAlive检测函数
  249. /// </summary>
  250. private static void StartKeepAlive()
  251. {
  252. theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
  253. }
  254. /// <summary>
  255. /// BeginReceive回调函数
  256. /// </summary>
  257. static byte[] buffer = new byte[1024];
  258. private static void OnReceiveCallback(IAsyncResult ar)
  259. {
  260. try
  261. {
  262. Socket peerSock = (Socket)ar.AsyncState;
  263. int BytesRead = peerSock.EndReceive(ar);
  264. if (BytesRead > 0)
  265. {
  266. byte[] tmp = new byte[BytesRead];
  267. Array.ConstrainedCopy(buffer, 0, tmp, 0, BytesRead);
  268. if (socketDataArrival != null)
  269. {
  270. socketDataArrival(tmp);
  271. }
  272. }
  273. else//对端gracefully关闭一个连接
  274. {
  275. if (theSocket.Connected)//上次socket的状态
  276. {
  277. if (socketDisconnected != null)
  278. {
  279. //1-重连
  280. socketDisconnected();
  281. //2-退出,不再执行BeginReceive
  282. return;
  283. }
  284. }
  285. }
  286. //此处buffer似乎要清空--待实现 zq
  287. theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
  288. }
  289. catch (Exception ex)
  290. {
  291. if (socketDisconnected != null)
  292. {
  293. socketDisconnected(); //Keepalive检测网线断开引发的异常在这里捕获
  294. return;
  295. }
  296. }
  297. }
  298. /// <summary>
  299. /// 异步收到消息处理器
  300. /// </summary>
  301. /// <param name="data"></param>
  302. private static void socketDataArrivalHandler(byte[] data)
  303. {
  304. }
  305. /// <summary>
  306. /// socket由于连接中断(软/硬中断)的后续工作处理器
  307. /// </summary>
  308. private static void socketDisconnectedHandler()
  309. {
  310. Reconnect();
  311. }
  312. /// <summary>
  313. /// 检测socket的状态
  314. /// </summary>
  315. /// <returns></returns>
  316. public static bool checkSocketState()
  317. {
  318. try
  319. {
  320. if (theSocket == null)
  321. {
  322. return socket_create_connect();
  323. }
  324. else if (IsconnectSuccess)
  325. {
  326. return true;
  327. }
  328. else//已创建套接字,但未connected
  329. {
  330. #region 异步连接代码
  331. TimeoutObject.Reset(); //复位timeout事件
  332. try
  333. {
  334. IPAddress ipAddress = IPAddress.Parse(remoteHost);
  335. IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
  336. theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
  337. SetXinTiao();//设置心跳参数
  338. }
  339. catch (Exception err)
  340. {
  341. SockErrorStr = err.ToString();
  342. return false;
  343. }
  344. if (TimeoutObject.WaitOne(2000, false))//直到timeout,或者TimeoutObject.set()
  345. {
  346. if (IsconnectSuccess)
  347. {
  348. return true;
  349. }
  350. else
  351. {
  352. return false;
  353. }
  354. }
  355. else
  356. {
  357. SockErrorStr = "Time Out";
  358. return false;
  359. }
  360. #endregion
  361. }
  362. }
  363. catch (SocketException se)
  364. {
  365. SockErrorStr = se.ToString();
  366. return false;
  367. }
  368. }
  369. /// <summary>
  370. /// 同步发送
  371. /// </summary>
  372. /// <param name="dataStr"></param>
  373. /// <returns></returns>
  374. public static bool SendData(byte[] buf)
  375. {
  376. bool result = false;
  377. if (buf == null || buf.Length < 0)
  378. return result;
  379. try
  380. {
  381. byte[] cmd = buf;
  382. int n = theSocket.Send(cmd);
  383. if (n < 1)
  384. result = false;
  385. }
  386. catch (Exception ee)
  387. {
  388. SockErrorStr = ee.ToString();
  389. result = false;
  390. }
  391. return result;
  392. }
  393. }
  394. }