NetComplexClient.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. using HslCommunication.Core;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using HslCommunication.Core.Net;
  8. using System.Net;
  9. using System.Net.Sockets;
  10. namespace HslCommunication.Enthernet
  11. {
  12. /// <summary>
  13. /// 一个基于异步高性能的客户端网络类,支持主动接收服务器的消息
  14. /// </summary>
  15. public class NetComplexClient : NetworkXBase
  16. {
  17. #region Constructor
  18. /// <summary>
  19. /// 实例化一个对象
  20. /// </summary>
  21. public NetComplexClient( )
  22. {
  23. }
  24. #endregion
  25. #region Private Member
  26. /// <summary>
  27. /// 客户端的核心连接块
  28. /// </summary>
  29. private AppSession stateone = new AppSession( );
  30. /// <summary>
  31. /// 指示客户端是否处于正在连接服务器中
  32. /// </summary>
  33. private bool IsClientConnecting = false;
  34. /// <summary>
  35. /// 登录服务器的判断锁
  36. /// </summary>
  37. private object lock_connecting = new object( );
  38. #endregion
  39. #region 基本属性块
  40. /// <summary>
  41. /// 客户端系统是否启动
  42. /// </summary>
  43. public bool IsClientStart { get; set; } = false;
  44. /// <summary>
  45. /// 重连接失败的次数
  46. /// </summary>
  47. public int ConnectFailedCount { get; set; } = 0;
  48. /// <summary>
  49. /// 客户端登录的标识名称,可以为ID号,也可以为登录名
  50. /// </summary>
  51. public string ClientAlias { get; set; } = "";
  52. /// <summary>
  53. /// 远程服务器的IP地址和端口
  54. /// </summary>
  55. public IPEndPoint EndPointServer { get; set; } = new IPEndPoint( IPAddress.Any, 0 );
  56. /// <summary>
  57. /// 服务器的时间,自动实现和服务器同步
  58. /// </summary>
  59. public DateTime ServerTime { get; private set; } = DateTime.Now;
  60. /// <summary>
  61. /// 系统与服务器的延时时间,单位毫秒
  62. /// </summary>
  63. public int DelayTime { get; private set; }
  64. #endregion
  65. #region 事件委托块
  66. /// <summary>
  67. /// 客户端启动成功的事件,重连成功也将触发此事件
  68. /// </summary>
  69. public event Action LoginSuccess;
  70. /// <summary>
  71. /// 连接失败时触发的事件
  72. /// </summary>
  73. public event Action<int> LoginFailed;
  74. /// <summary>
  75. /// 服务器的异常,启动,等等一般消息产生的时候,出发此事件
  76. /// </summary>
  77. public event Action<string> MessageAlerts;
  78. /// <summary>
  79. /// 在客户端断开后并在重连服务器之前触发,用于清理系统资源
  80. /// </summary>
  81. public event Action BeforReConnected;
  82. /// <summary>
  83. /// 当接收到文本数据的时候,触发此事件
  84. /// </summary>
  85. public event Action<AppSession, NetHandle, string> AcceptString;
  86. /// <summary>
  87. /// 当接收到字节数据的时候,触发此事件
  88. /// </summary>
  89. public event Action<AppSession, NetHandle, byte[]> AcceptByte;
  90. #endregion
  91. #region 启动停止重连块
  92. private bool IsQuie { get; set; } = false;
  93. /// <summary>
  94. /// 关闭该客户端引擎
  95. /// </summary>
  96. public void ClientClose( )
  97. {
  98. IsQuie = true;
  99. if (IsClientStart)
  100. SendBytes( stateone, HslProtocol.CommandBytes( HslProtocol.ProtocolClientQuit, 0, Token, null ) );
  101. thread_heart_check?.Abort( );
  102. IsClientStart = false;
  103. Thread.Sleep( 5 );
  104. LoginSuccess = null;
  105. LoginFailed = null;
  106. MessageAlerts = null;
  107. AcceptByte = null;
  108. AcceptString = null;
  109. stateone.WorkSocket?.Close( );
  110. LogNet?.WriteDebug( ToString(), "Client Close." );
  111. }
  112. /// <summary>
  113. /// 启动客户端引擎,连接服务器系统
  114. /// </summary>
  115. public void ClientStart( )
  116. {
  117. if (IsClientStart) return;
  118. Thread thread_login = new Thread( new ThreadStart( ThreadLogin ) );
  119. thread_login.IsBackground = true;
  120. thread_login.Start( );
  121. LogNet?.WriteDebug( ToString( ), "Client Start." );
  122. if (thread_heart_check == null)
  123. {
  124. thread_heart_check = new Thread( new ThreadStart( ThreadHeartCheck ) );
  125. thread_heart_check.IsBackground = true;
  126. thread_heart_check.Start( );
  127. }
  128. }
  129. private void ThreadLogin( )
  130. {
  131. lock (lock_connecting)
  132. {
  133. if (IsClientConnecting) return;
  134. IsClientConnecting = true;
  135. }
  136. if (ConnectFailedCount == 0)
  137. {
  138. MessageAlerts?.Invoke( "正在连接服务器..." );
  139. }
  140. else
  141. {
  142. int count = 10;
  143. while (count > 0)
  144. {
  145. if (IsQuie) return;
  146. MessageAlerts?.Invoke( "连接断开,等待" + count-- + "秒后重新连接" );
  147. Thread.Sleep( 1000 );
  148. }
  149. MessageAlerts?.Invoke( "正在尝试第" + ConnectFailedCount + "次连接服务器..." );
  150. }
  151. stateone.HeartTime = DateTime.Now;
  152. LogNet?.WriteDebug( ToString( ), "Begin Connect Server, Times: " + ConnectFailedCount );
  153. OperateResult result = new OperateResult( );
  154. OperateResult<Socket> connectResult = CreateSocketAndConnect( EndPointServer, 10000 );
  155. if (!connectResult.IsSuccess)
  156. {
  157. ConnectFailedCount++;
  158. IsClientConnecting = false;
  159. LoginFailed?.Invoke( ConnectFailedCount );
  160. LogNet?.WriteDebug( ToString( ), "Connected Failed, Times: " + ConnectFailedCount );
  161. // 连接失败,重新连接服务器
  162. ReconnectServer( );
  163. return;
  164. }
  165. // 连接成功,发送数据信息
  166. OperateResult sendResult = SendStringAndCheckReceive( connectResult.Content, 1, ClientAlias );
  167. if (!sendResult.IsSuccess)
  168. {
  169. ConnectFailedCount++;
  170. IsClientConnecting = false;
  171. LogNet?.WriteDebug( ToString( ), "Login Server Failed, Times: " + ConnectFailedCount );
  172. LoginFailed?.Invoke( ConnectFailedCount );
  173. // 连接失败,重新连接服务器
  174. ReconnectServer( );
  175. return;
  176. }
  177. // 登录成功
  178. ConnectFailedCount = 0;
  179. stateone.IpEndPoint = (IPEndPoint)connectResult.Content.RemoteEndPoint;
  180. stateone.LoginAlias = ClientAlias;
  181. stateone.WorkSocket = connectResult.Content;
  182. stateone.WorkSocket.BeginReceive( stateone.BytesHead, stateone.AlreadyReceivedHead,
  183. stateone.BytesHead.Length - stateone.AlreadyReceivedHead, SocketFlags.None,
  184. new AsyncCallback( HeadBytesReceiveCallback ), stateone );
  185. // 发送一条验证消息
  186. // SendBytes(stateone, CommunicationCode.CommandBytes(CommunicationCode.Hsl_Protocol_Check_Secends));
  187. byte[] bytesTemp = new byte[16];
  188. BitConverter.GetBytes( DateTime.Now.Ticks ).CopyTo( bytesTemp, 0 );
  189. SendBytes( stateone, HslProtocol.CommandBytes( HslProtocol.ProtocolCheckSecends, 0, Token, bytesTemp ) );
  190. stateone.HeartTime = DateTime.Now;
  191. IsClientStart = true;
  192. LoginSuccess?.Invoke( );
  193. LogNet?.WriteDebug( ToString( ), "Login Server Success, Times: " + ConnectFailedCount );
  194. IsClientConnecting = false;
  195. Thread.Sleep( 1000 );
  196. }
  197. // private bool Is_reconnect_server = false;
  198. // private object lock_reconnect_server = new object();
  199. private void ReconnectServer( )
  200. {
  201. // 是否连接服务器中,已经在连接的话,则不再连接
  202. if (IsClientConnecting) return;
  203. // 是否退出了系统,退出则不再重连
  204. if (IsQuie) return;
  205. LogNet?.WriteDebug( ToString( ), "Prepare ReConnect Server." );
  206. // 触发连接失败,重连系统前错误
  207. BeforReConnected?.Invoke( );
  208. stateone.WorkSocket?.Close( );
  209. Thread thread_login = new Thread( new ThreadStart( ThreadLogin ) )
  210. {
  211. IsBackground = true
  212. };
  213. thread_login.Start( );
  214. }
  215. #endregion
  216. #region 发送接收块
  217. /// <summary>
  218. /// 通信出错后的处理
  219. /// </summary>
  220. /// <param name="receive"></param>
  221. /// <param name="ex"></param>
  222. internal override void SocketReceiveException( AppSession receive, Exception ex )
  223. {
  224. if (ex.Message.Contains( StringResources.SocketRemoteCloseException ))
  225. {
  226. // 异常掉线
  227. ReconnectServer( );
  228. }
  229. else
  230. {
  231. // MessageAlerts?.Invoke("数据接收出错:" + ex.Message);
  232. }
  233. LogNet?.WriteDebug( ToString( ), "Socket Excepiton Occured." );
  234. }
  235. /// <summary>
  236. /// 服务器端用于数据发送文本的方法
  237. /// </summary>
  238. /// <param name="customer">用户自定义的命令头</param>
  239. /// <param name="str">发送的文本</param>
  240. public void Send( NetHandle customer, string str )
  241. {
  242. if (IsClientStart)
  243. {
  244. SendBytes( stateone, HslProtocol.CommandBytes( customer, Token, str ) );
  245. }
  246. }
  247. /// <summary>
  248. /// 服务器端用于发送字节的方法
  249. /// </summary>
  250. /// <param name="customer">用户自定义的命令头</param>
  251. /// <param name="bytes">实际发送的数据</param>
  252. public void Send( NetHandle customer, byte[] bytes )
  253. {
  254. if (IsClientStart)
  255. {
  256. SendBytes( stateone, HslProtocol.CommandBytes( customer, Token, bytes ) );
  257. }
  258. }
  259. private void SendBytes( AppSession stateone, byte[] content )
  260. {
  261. SendBytesAsync( stateone, content );
  262. }
  263. #endregion
  264. #region 信息处理中心
  265. /// <summary>
  266. /// 客户端的数据处理中心
  267. /// </summary>
  268. /// <param name="session"></param>
  269. /// <param name="protocol"></param>
  270. /// <param name="customer"></param>
  271. /// <param name="content"></param>
  272. internal override void DataProcessingCenter( AppSession session, int protocol, int customer, byte[] content )
  273. {
  274. if (protocol == HslProtocol.ProtocolCheckSecends)
  275. {
  276. DateTime dt = new DateTime( BitConverter.ToInt64( content, 0 ) );
  277. ServerTime = new DateTime( BitConverter.ToInt64( content, 8 ) );
  278. DelayTime = (int)(DateTime.Now - dt).TotalMilliseconds;
  279. stateone.HeartTime = DateTime.Now;
  280. // MessageAlerts?.Invoke("心跳时间:" + DateTime.Now.ToString());
  281. }
  282. else if (protocol == HslProtocol.ProtocolClientQuit)
  283. {
  284. // 申请了退出
  285. }
  286. else if (protocol == HslProtocol.ProtocolUserBytes)
  287. {
  288. // 接收到字节数据
  289. AcceptByte?.Invoke( stateone, customer, content );
  290. }
  291. else if (protocol == HslProtocol.ProtocolUserString)
  292. {
  293. // 接收到文本数据
  294. string str = Encoding.Unicode.GetString( content );
  295. AcceptString?.Invoke( stateone, customer, str );
  296. }
  297. }
  298. #endregion
  299. #region 心跳线程块
  300. private Thread thread_heart_check { get; set; } = null;
  301. /// <summary>
  302. /// 心跳线程的方法
  303. /// </summary>
  304. private void ThreadHeartCheck( )
  305. {
  306. Thread.Sleep( 2000 );
  307. while (true)
  308. {
  309. Thread.Sleep( 1000 );
  310. if (!IsQuie)
  311. {
  312. byte[] send = new byte[16];
  313. BitConverter.GetBytes( DateTime.Now.Ticks ).CopyTo( send, 0 );
  314. SendBytes( stateone, HslProtocol.CommandBytes( HslProtocol.ProtocolCheckSecends, 0, Token, send ) );
  315. double timeSpan = (DateTime.Now - stateone.HeartTime).TotalSeconds;
  316. if (timeSpan > 1 * 8)//8次没有收到失去联系
  317. {
  318. LogNet?.WriteDebug( ToString( ), $"Heart Check Failed int {timeSpan} Seconds." );
  319. ReconnectServer( );
  320. Thread.Sleep( 1000 );
  321. }
  322. }
  323. else
  324. {
  325. break;
  326. }
  327. }
  328. }
  329. #endregion
  330. #region Object Override
  331. /// <summary>
  332. /// 返回对象的字符串表示形式
  333. /// </summary>
  334. /// <returns></returns>
  335. public override string ToString( )
  336. {
  337. return "NetComplexClient";
  338. }
  339. #endregion
  340. }
  341. }