Implementation of the alternating bit protocol for reliability. Assume a mail fits in a packet, that is, no fragmentation. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% network/post.h %%%%%%%%%%%% Introduce two fields: sequence number and type, into MailHeader class MailHeader { public: MailBoxAddress to; MailBoxAddress from; unsigned length; //------------ change ---------- unsigned seqNo; // sequence number, 0 or 1 enum type {MSG, ACK}; //-------- end of change ------- }; %%%%%%%%%%%% Define constants: Max number of trials and time out for acknowledgement #define MaxMailSize (MaxPacketSize - sizeof(MailHeader)) //------------ change ---------- #define MaxSends 10 #define TimeOut (NetworkTime * 20) //-------- end of change -------- %%%%%%%%%%%% Introduce an input sequence number class PostOfficeInput : public CallBackObj { public: ... private: ... //----------- change ----------- unsigned inSeqNo = 0; //-------- end of change ------- }; %%%%%%%%%%%% Introduce an output sequence number and a boolean acked, a variable nSends to track the number of trials, and a lock poLock for mutual exclusion (only one packet can be sent at a time) class PostOfficeOutput : public CallBackObj { public: ... //------------ change ----------- unsigned outSeqNo = 0; // 0 or 1 bool acked; //-------- end of change ------- private: ... //----------- change ----------- int nSends; Lock poLock; //-------- end of change ------- }; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% network/post.cc %%%%%%%%%%%% When the postal worker receives a packet, it checks the mail header. If it is an acknowlegement and the sequence number matches, the packet being held is acknowledged and the sender is signaled. If it is a regular mail, it sends an acknowledgement back, if the sequence number matches, toggles the input sequence number and delivers the mail. void PostOfficeInput::PostalDelivery() { PacketHeader pktHdr; MailHeader mailHdr; char *buffer = new char[MaxPacketSize]; //-------------- change -------------- unsign inSeqNo = 0; //-------- end of change ------------- for (;;) { // first, wait of a message messageAvailable ->P(); pktHdr = network->Receive(buffer); mailHdr = *(MailHeader *)buffer; if (debug->IsEnabled('n')) { cout ... ... } // check that arriving message is legal ASSERT(mailHdr.length <= MaxMailSize); //------------- change ------------- if (mailHdr.type == ACK) if (mailHdr.seqNo == postOfficeOutput->outSeqNo) { postOfficeOutput->acked = TRUE; // call alarm::AckSignal; } else { // mailHdr.type == MSG) // update the packet header and mail header // call PostOfficeOutput::SendAck to send an acknowledgement if (mailHdr.seqNo == inSeqNo) { // toggle inSeqNo //--------- end of change ----------- // check that arriving message is legal ASSERT(0 <= mailHdr.to && mailHdr.to <= numBoxes); // put into mailbox ... } } } delete buffer; } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Send regular mails, called by the user. After a packet is sent to the network, the sender holds the packet. If the sender is signaled by acknowledgement from the postal worker, it toggles the output sequence number and returns. If the sender is signaled by time out by a timer interrupt (Alarm::CallBack), it resends the packet. void PostOfficeOutput::Send(...) { char* buffer ... ... //-------------- change ------------- if (mailHdr.type == MSG) mailHdr.seqNo = outSeqNo; //---------- end of change ---------- // fill in pktHdr, for network layer ... // concatenate MailHeader and data ... poLock->Acquire(); nSends = 0; acked = FALSE; while (nSends < MaxSends) { sendLock->Acquire() network->Send(pckHdr, buffer); messageSent->P(); sendLock->Release(); nSends ++; // call alarm ackWait(totalticks + TimeOut); if (acked) { // toggle outSeqNo break; } } if (nSends >= MaxSends) { // error('Network down') } poLock->Release(); delete buffer; } %%%%%%%%%%%%%% Send acknowledgements, called by the postal worker void PostOfficeOutput::SendAck(...) { char* buffer ... ... // concatenate MailHeader and data ... sendLock->Acquire() network->Send(pckHdr, buffer); messageSent->P(); sendLock->Release(); delete buffer; } %%%%%%%%%%%%%%%%%%% changes in Alarm %%%%%%%%%%%%%%%% When a timer interrup occurs, the timer interrupt handler (Alarm::CallBack) checks post office send time out. class Alarm : public CallBackObj { public: ... //--------------- change -------------- void AckWait(int x); void AckSignal(); //----------- end of change ----------- private: Timer *time; //--------------- change ------------- Lock *ackLock; Condition *ackCond; int ackWhen; //---------- end of change ----------- void CallBack(); }; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% threads/alarm.cc Alarm::AckWait(int x) { ackWhen = x; ackLock->Acquire(); ackCond->Wait(); ackLock->Release; } Alarm::AckSignal() { ackLock->Acquire(); ackCond->Signal(); ackLock->Release; } Alarm::CallBack() { //------------ change for network -------------- if (ackWhen < = totalticks) { ackLock->Acquire(); ackCond->Signal(); ackLock->Release; } //--------------- end of change --------------- ... }