Main Page | Class Hierarchy | Class List | File List | Class Members

SmartDeviceClient/SmartProtocolStack/RemoteHost/ACKManager.cs

00001 using System;
00002 using System.Threading;
00003 using System.Collections;
00004 using System.Collections.Specialized;
00005 using GPRSWeb.SmartDeviceClient.Common;
00006 
00007 namespace GPRSWeb.SmartDeviceClient.SmartProtocolStack.RemoteHost
00008 {
00009 
00010         public class ACKManager {
00011                 public static int INTER_ACKABLE_TIME;
00012                 public static int FIRST_ACKABLE_TIME;
00013                 public static int ACK_ARRIVAL_TIMEOUT;
00014 
00015                 private AckQueue ackQueue;
00016                 private ReplyTracker replyTracker;
00017                 private AckTimerList ackTimerList;
00018                 private AckableGenerator ackableGenerator;
00019                 private RemoteHostComms thisRemoteHost;
00020 
00021                 public ACKManager(RemoteHostComms thisRemoteHost) {                     
00022                         this.thisRemoteHost = thisRemoteHost;
00023                         Init();
00024                         ackQueue = new AckQueue();
00025                         replyTracker = new ReplyTracker();
00026                         ackTimerList = new AckTimerList(replyTracker, thisRemoteHost.thisStack.connectionReset);
00027                         ackableGenerator = new AckableGenerator(thisRemoteHost);
00028                 }
00029 
00030                 public void Init() {
00031                         INTER_ACKABLE_TIME = thisRemoteHost.thisStack.settings.InterAckableTime; // seconds
00032                         FIRST_ACKABLE_TIME = thisRemoteHost.thisStack.settings.FirstAckableTime; // seconds
00033                         ACK_ARRIVAL_TIMEOUT = thisRemoteHost.thisStack.settings.AckArrivalTimeout; // seconds
00034                 }
00035 
00036                 public void ResetConnection() {
00037                         StopAckableGenerator();
00038             ackQueue.Clear();
00039                         ackTimerList.ResetConnection();
00040                         replyTracker.ResetConnection();
00041                         StartAckableGenerator();
00042                 }
00043 
00048                 public bool IsAckableWaiting() {
00049                         /* if we are due to send an ackable packet, this will return true. Else it will not.
00050                          * should maintain a count of ackable packets waiting (semaphore style?) */
00051                         return ackableGenerator.IsAckableWaiting;
00052                 }
00053 
00054                 public void StartAckableGenerator() {
00055                         ackableGenerator.Start();
00056                 }
00057 
00058                 public void StopAckableGenerator() {
00059                         ackableGenerator.Stop();
00060                 }
00061                 
00062                 public AckInfo GetNextAck() {
00063                         /* returns details of the next acknowledgement that needs to be sent, 
00064                          * or null if no acknowledgement needs sending */
00065                         if (ackQueue.IsEmpty) { return null; } 
00066                         else return ackQueue.Dequeue(); 
00067                 }
00068 
00073                 public void SentAckableSegment(uint sequenceNumber) {
00074                         ackableGenerator.DecAckablesWaiting();
00075                         if (thisRemoteHost.readyToSendUserData) {
00076                                 ackTimerList.AddTimer(sequenceNumber);
00077                         } else {
00078                                 Console.WriteLine("    Did not set timer for return of ACK, since not ready to send");
00079                         }
00080                         //Console.WriteLine("ACK Manager: Ackable Segment #" + sequenceNumber +" just Sent");
00081                 }
00082 
00087                 public void ProcessReceivedSegmentHeaders(ref SegmentHeaders headers) {
00088                         /* if segment is an ACK, call SegmentAcknowledged(seqNum) 
00089                          * if segment is ACKable, call IssueACK(seqNum) 
00090                          * otherwise noop*/
00091                         if (headers.IsAck) { this.SegmentAcknowledged(headers.ACKdSequenceNumber); }
00092                         if (headers.IsAckable) { this.IssueACK(headers.SequenceNumber); }
00093                 }
00094 
00099                 private void SegmentAcknowledged(uint ackdSequenceNumber) {
00100                         /* If a timer for the segment has not yet expired: 
00101                          * - clear the timer
00102                          * - reset the 'Number of Acks missed in a row' counter
00103                          * else 
00104                          * - do nothing */
00105                         if (thisRemoteHost.readyToSendUserData) {
00106                                 if (ackTimerList.Contains(ackdSequenceNumber)) {
00107                                         ackTimerList.RemoveTimer(ackdSequenceNumber);
00108                                         replyTracker.ACKReceived();
00109                                         //Console.WriteLine("ACK Manager: ACK received for segment#" + ackdSequenceNumber);
00110                                 }
00111                         } else {
00112                                 thisRemoteHost.readyToSendUserData = true;
00113                                 Console.WriteLine("### First ACK received. Now READY TO SEND USER DATA ###");
00114                         }
00115                         
00116                 }
00117 
00118                 private void IssueACK(uint sequenceNumberToACK) {
00119                         //Console.WriteLine("ACK Manager: Ackable received. Need to ACK #" + sequenceNumberToACK);
00120                         lock(thisRemoteHost.TxQueue.SyncRoot) {
00121                                 if (thisRemoteHost.TxQueue.IsEmpty) {
00122                                         /* create no-op message */
00123                                         Message noopMsg = new EmptyMessage();
00124                                         noopMsg.Destination = thisRemoteHost.RemoteDeviceID;
00125                                         /* segmentise */
00126                                         TransmissionQueueEntry[] buffer = TransmissionQueueEntry.FromMessage(noopMsg);
00127                                         /* place results in segment queue */
00128                                         // make sure this all happens in a monitor, so we get them all together
00129                                         thisRemoteHost.TxQueue.EnqueueUnprotected(buffer, false);
00130                                 }
00131                                 ackQueue.Enqueue(new AckInfo(sequenceNumberToACK));
00132                                 //Console.WriteLine("ACK Manager: ACK Issued for #" + sequenceNumberToACK);
00133                                 Monitor.PulseAll(thisRemoteHost.TxQueue.SyncRoot);
00134                         }
00135                         
00136                 }
00137         }
00138 
00139         public class AckableGenerator {
00140                 private int ackablesWaitingCount;
00141                 private Timer ackableGenerateTimer;
00142                 private RemoteHostComms thisRemoteHost;
00143                 private TransmissionQueue txQueue;
00144                 
00149                 public AckableGenerator(RemoteHostComms thisRemoteHost) {
00150                         this.thisRemoteHost = thisRemoteHost;
00151                         this.txQueue = thisRemoteHost.TxQueue;
00152                         ackablesWaitingCount = 0;
00153                         ackableGenerateTimer = new Timer(new TimerCallback(this.GeneratorTimerCallback), null, Timeout.Infinite, Timeout.Infinite);
00154                 }
00155 
00156                 public void GeneratorTimerCallback(object o) {
00157                         lock(txQueue.SyncRoot) {
00158                                 if (txQueue.IsEmpty) {
00159                                         /* create no-op message */
00160                                         Message noopMsg = new EmptyMessage();
00161                                         noopMsg.Destination = thisRemoteHost.RemoteDeviceID;
00162                                         /* segmentise */
00163                                         TransmissionQueueEntry[] buffer = TransmissionQueueEntry.FromMessage(noopMsg);
00164                                         /* place results in segment queue */
00165                                         // make sure this all happens in a monitor, so we get them all together
00166                                         txQueue.EnqueueUnprotected(buffer, false);
00167                                 }
00168                                 IncAckablesWaiting();
00169                                 lock (thisRemoteHost.AckManager) {
00170                                         Monitor.PulseAll(thisRemoteHost.AckManager);
00171                                 }
00172                                 Monitor.PulseAll(txQueue.SyncRoot);
00173                         }
00174                 }
00175 
00176                 public void IncAckablesWaiting() {
00177                         lock(this) {
00178                                 ackablesWaitingCount++;
00179                         }
00180                 }
00181                 public void DecAckablesWaiting() {
00182                         lock(this) {
00183                                 if (ackablesWaitingCount > 0) { ackablesWaitingCount--; }
00184                         }
00185                 }
00186 
00187                 public void ResetConnection() {
00188                         lock(this) {
00189                                 ackablesWaitingCount = 0;
00190                         }
00191                 }
00192 
00193                 public void Start() {
00194                         ackableGenerateTimer.Change(ACKManager.FIRST_ACKABLE_TIME * 1000, ACKManager.INTER_ACKABLE_TIME * 1000);
00195                 }
00196 
00197                 public void Stop() {
00198                         ackableGenerateTimer.Change(Timeout.Infinite, Timeout.Infinite);
00199                 }
00200 
00201                 public bool IsAckableWaiting {
00202                         get {
00203                                 bool result;
00204                                 lock(this) {
00205                                         result = (ackablesWaitingCount != 0);
00206                                 }
00207                                 return result;
00208                         }
00209                 }
00210 
00211         }
00212 
00213         public class AckInfo {
00215                 uint sequenceNumber; // seq # of the segment that needs acking
00216 
00218                 public AckInfo(uint sequenceNumber) {
00219                         this.sequenceNumber = sequenceNumber;
00220                 }
00221 
00223                 public uint SequenceNumber { get { return sequenceNumber; } }
00224         }
00225 
00226         class AckQueue : Queue {
00227                 public AckQueue() : base() {}
00228 
00229                 public void Enqueue(AckInfo ackInfo) {
00230                         lock (this.SyncRoot) {
00231                                 base.Enqueue(ackInfo);                  
00232                                 Monitor.PulseAll(this.SyncRoot);
00233                         } 
00234                 }
00235 
00236                 public new AckInfo Dequeue() {
00237                         AckInfo result;
00238                         lock (this.SyncRoot) {
00239                                 result = (AckInfo)base.Dequeue();
00240                         }
00241                         return result;
00242                 }
00243 
00244 
00245                 public bool IsEmpty {
00246                         get {
00247                                 bool result;
00248                                 lock (this.SyncRoot) {
00249                                         result = (this.Count == 0);
00250                                 }
00251                                 return result;
00252                         }
00253                 }
00254 
00255         }
00256 
00257         class AckTimer {
00258                 private Timer t;
00259 
00260                 public AckTimer(TimerInfo timerInfo, TimerCallback callback) {
00261                         t = new Timer(callback, timerInfo, Timeout.Infinite, Timeout.Infinite);
00262                 }
00263 
00264                 public void StopTheClock() {
00265                         t.Change(Timeout.Infinite, Timeout.Infinite);
00266                         //t.Dispose();
00267                         //t = null; // cause garbage collecter to free some memory.
00268                         //Console.WriteLine("Clock Stopped");
00269                 }
00270 
00275                 public void StartTheClock(uint countdown) {
00276                         t.Change(countdown, Timeout.Infinite);
00277                         //Console.WriteLine("Clock Started");
00278                 }
00279         }
00280 
00285         class ReplyTracker {
00286                 private int noReplyCount;
00287                 public ReplyTracker() {
00288                         Init();
00289                 }
00290 
00291                 private void Init() {
00292                         noReplyCount = 0;
00293                 }
00294 
00295                 public void ResetConnection() {
00296                         Init();
00297                 }
00298 
00299                 public void ACKMissed() {
00300                         noReplyCount++;
00301                         if (noReplyCount == 3) {
00302                                 throw new ConnectionReset();
00303                         }
00304                 }
00305 
00306                 public void ACKReceived() {
00307                         noReplyCount = 0;
00308                 }
00309         }
00310 
00314         class AckTimerList : ListDictionary {
00315                 private ReplyTracker replyTracker;
00316                 private ConnectionResetDelegate connectionReset;
00317 
00318                 public AckTimerList(ReplyTracker replyTracker, ConnectionResetDelegate connectionReset) : base() {
00319                         this.replyTracker = replyTracker;
00320                         this.connectionReset = connectionReset;
00321                 }
00322 
00328                 public void AddTimer(uint sequenceNumber) {
00329                         TimerInfo info = new TimerInfo(sequenceNumber, replyTracker);
00330                         AckTimer timerObj = new AckTimer(info, new TimerCallback(this.TimerExpires));
00331                         base.Add(sequenceNumber, timerObj);
00332                         timerObj.StartTheClock((uint)ACKManager.ACK_ARRIVAL_TIMEOUT * 1000);
00333                 }
00334 
00339                 public void TimerExpires(object obj){
00340                         try {
00341                                 TimerInfo ti = (TimerInfo)obj;
00342                                 lock(ti.tracker) {
00343                                         ti.tracker.ACKMissed();
00344                                 }
00345                                 this.RemoveTimer(ti.segmentNumber); 
00346                         } catch (ConnectionReset) {
00347                                 Console.WriteLine("*** No traffic on connection for last 90 seconds. Connection Reset ***\n*** Calling Connection Reset Handler ***");
00348                                 connectionReset();
00349                         }
00350                 }
00351 
00356                 public void RemoveTimer(uint sequenceNumber) {
00357                         AckTimer workingTimer = (AckTimer)base[sequenceNumber];
00358                         if (workingTimer != null) {
00359                                 workingTimer.StopTheClock();
00360                         }
00361                         base.Remove(sequenceNumber);
00362                 }
00363 
00367                 public void ResetConnection() {
00368                         foreach (DictionaryEntry de in this) {
00369                                 AckTimer timer = (AckTimer)de.Value;
00370                                 timer.StopTheClock();
00371                         }
00372                         Clear();
00373                 }
00374         }
00375 
00381         class TimerInfo {
00382                 public uint segmentNumber;
00383                 public ReplyTracker tracker;
00384 
00385                 public TimerInfo(uint segmentNumber, ReplyTracker tracker) {
00386                         this.segmentNumber = segmentNumber;
00387                         this.tracker = tracker;
00388                 }
00389         }
00390 }

Generated on Mon May 8 22:07:27 2006 by  doxygen 1.3.9.1