goBackN.js

/** @module goBackN */

import {
    AbstractSequentialReceiver,
    AbstractWindowedSender
} from "./abstractNodes.js";
import Packet from "./Packet.js";


/**
 * A class that represents a *go-back-n* sender.
 * @extends AbstractWindowedSender
 */
export class GBNSender extends AbstractWindowedSender{

    /**
     * Creates a new GBNSender instance.
     * @param {Object} options - The constructor options.
     * @param {GBNReceiver} options.receiver - The *go-back-n* receiver.
     * @param {number} options.timeout - The time to wait for an acknowledgment
     * from the receiver.
     * @param {Channel} options.channel - The channel through which to send the
     * packets.
     * @param {number} options.windowSize - The size of the sliding window.
     */
    constructor({
        receiver,
        timeout,
        channel,
        windowSize
    }){
        super({
            receiver,
            timeout,
            channel,
            windowSize
        });
    }

    /**
     * Tests whether a received packet is correct or not. A packet is correct if
     * it's an acknowledgment, it's not corrupted and the acknowledged sequence
     * number corresponds to one of the sequences in the set of sent packets
     * from the current window.
     * @param {Packet} packet - The packet to test.
     * @returns {boolean} - true if packet is correct, false otherwise.
     * @protected
     * @override
     */
    _checkReceivedPkt(packet){
        const {isCorrupted, isAck, ackNum} = packet;
        return !isCorrupted && isAck && this._isInExpectedRange(ackNum);
    }

    /**
     * Processes a correctly received packet, confirming the acknowledged
     * sequence and all previous ones. It moves the window and stops the timeout
     * or, if there are still unacknowledged sequences in the window, resets it.
     * @param {Packet} packet - The packet to process.
     * @protected
     * @override
     */
    _processExpectedPkt(packet){
        const {base, nextSeqNum} = this;
        const newBase = this._nextSequence(packet.ackNum);
        for(let i=base; i!=newBase; i=this._nextSequence(i))
            this.onPktConfirmed(i);
        this._unsetTimeout(base);
        this._setBase(newBase);
        if(newBase != nextSeqNum)
            this._setTimeout(newBase);
    }

    /**
     * Resends the previously sent packets from the current window, and restarts
     * the timeout.
     * @param {number} seqNum - The sequence number for which the timeout
     * passed.
     * @protected
     * @override
     */
    _onTimeout(seqNum){
        const {nextSeqNum} = this;
        for(let i=seqNum; i!=nextSeqNum; i=this._nextSequence(i))
            this._sendPkt(new Packet({
                seqNum: i,
                sender: this,
                receiver: this._receiver,
                wasReSent: true
            }));
        this._setTimeout(seqNum);
    }

    /**
     * Sends a packet and starts a timeout if it is the first of the window.
     * @protected
     * @override
     */
    _processSending(){
        const {base, nextSeqNum} = this;
        this._sendPkt(new Packet({
            sender: this,
            receiver: this._receiver,
            seqNum: nextSeqNum
        }));
        if(base == nextSeqNum)
            this._setTimeout(nextSeqNum);
        this._nextSeqNum = this._nextSequence(nextSeqNum);
    }
}


/**
 * A class that represents a *go-back-n* receiver.
 * @extends AbstractSequentialReceiver
 */
export class GBNReceiver extends AbstractSequentialReceiver{

    /**
     * Creates a new GBNReceiver instance.
     * @param {Object} options - The constructor options.
     * @param {Channel} options.channel - The channel through which to send the
     * acknowledgments.
     * @param {number} options.windowSize - The size of the window used by the
     * sender.
     */
    constructor({
        channel,
        windowSize
    }){
        super({
            channel,
            seqNumLimit: windowSize * 2
        });
    }
}