/// <summary> /// receive date stream through socket /// </summary> /// <param name="control"></param> private void __threadListen(ThreadControl control) { logger.Debug($"{nameof(__threadListen)}() on duty."); var count = 1; // the actual received count of bytes by socket var lenBuff = new byte[sizeof(uint)]; // store the message length number in byte format var slot = new byte[slotSize]; // reception slot, it could maintain the integral data of a message int k = 0, l = 0; // pointer for slot, pointer for lenBuff uint msgLen = 0, verifyLen = 0; // message header, check code var rbuffer = new byte[bufferSize]; // reception buffer var slotBackup = slot; // switch back to the primal slot after using super slot socket.SendBufferSize = this.bufferSize; while (!control.SafelyTerminating) { /******************************* * receive bytes through socket * *****************************/ rwlsSocket.EnterReadLock(); try { var result = socket.BeginReceive(rbuffer, 0, rbuffer.Length, SocketFlags.None, x => x = null, null); // async reception,to realize 'SafelyTermination' while (!control.SafelyTerminating && !result.IsCompleted) // wait for completion { Thread.Sleep(1); } if (control.SafelyTerminating || !socket.Connected) { break; // termination signal detected, stop listening } count = socket.EndReceive(result); } catch (SocketException se) { logger.Error($"{se.Message}"); break; } catch (Exception ex) { logger.Fatal("fatal error occurred in __threadListen.", ex); Catch(new EOCException(ex)); break; } finally { rwlsSocket.ExitReadLock(); } /***************************** * process the received bytes * ***************************/ for (var j = 0; j < count;) // deal with the bytes received in rbuffer { if (msgLen == 0) { lenBuff[l] = rbuffer[j]; ++j; // point to the 1st byte of content if (++l == lenBuff.Length) { l = 0; msgLen = BitConverter.ToUInt32(lenBuff, 0); if (verifyLen != 0) // need validation { if (verifyLen == msgLen) // validatin OK { watchDog = 0; // feed local dog, reset the timer if (msgLen == 1) // system command code { if (slot[0] == DOG_FOOD_FLAG) // dog food { // pass } } else { var msg = EMessage.FromBytes(slot, 0, k); ThreadPool.QueueUserWorkItem(_processInMessage, msg); } msgLen = 0; } else { Catch(new EOCException("transmission error, please reconnect!")); } verifyLen = 0; if (slot.Length != slotSize) { slot = slotBackup; } } else { if (msgLen > slotSize) { slot = new byte[msgLen]; // super slot } } k = 0; } } else { int n = (int)(msgLen - k); if (n + j > count) { n = count - j; } Array.Copy(rbuffer, j, slot, k, n); k += n; j += n; if (k == msgLen) { verifyLen = msgLen; msgLen = 0; } } } } control.SetAbortedFlags(); OnThreadListenAborted(); logger.Debug($"{nameof(__threadListen)}() aborted."); }
/// <summary> /// watch dog has the highest authority in agent, it performs some light weight operations, such as update 'TCounters' /// </summary> private void __threadWatchdog(ThreadControl control) { logger.Debug($"{nameof(__threadWatchdog)}() on duty."); var feedCycle = Math.Min(1000, this.timeout >> 1); // the cycle of dog feeding, unit: ms var feedTCounter = 0; var msg = new EMessage(0); var foodBag = new[] { DOG_FOOD_FLAG }; var threadInterval = 1; // watch dog scan cycle, ms while (!control.SafelyTerminating) { Thread.Sleep(threadInterval); // -1 denote that the dog is dead,only __threadListen can set it back to '0' to rebirth the dog if (watchDog == -1) { continue; // no more actions when dog has died } /******************* * update TCounters * *****************/ KeyValuePair <uint, TCounter>[] msgIdAndTCounterPairs; lock (dictMsgId2TC) msgIdAndTCounterPairs = dictMsgId2TC.ToArray(); foreach (var kv in msgIdAndTCounterPairs) { if (kv.Value.Decrease(threadInterval) || !this.IsConnected) // request timeout { kv.Value.CountDown = 0; // set countdown to 'timeout' RemoveWaitFlag(kv.Key); ThreadPool.QueueUserWorkItem(_processTimeoutMessage, kv.Value.RequestMsg); } } /************ * watch dog * **********/ if (IsConnected) { // check local dog watchDog += threadInterval; if (watchDog >= timeout) { watchDog = -1; // time out flag GetControl(ThreadType.Listen).SafeAbort(); OnConnectionTimeout(); logger.Error($"{this} connection timeout."); } // feed remote dog feedTCounter += threadInterval; if (feedTCounter >= feedCycle) { feedTCounter = 0; // reset try { SendBytes(foodBag, 0, foodBag.Length); } catch { // pass, ignore all errors } } } } control.SetAbortedFlags(); logger.Debug($"{nameof(__threadWatchdog)}() aborted."); this.Destroy(); this.Phase = ConnectionPhase.P0Start; }