Skip to content

Commit

Permalink
Refactored TCPPacketGenerator. (#28)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Baochun Li <bli@ece.toronto.edu>
  • Loading branch information
cying17 and baochunli authored Feb 6, 2024
1 parent e2511cd commit 58fe750
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 103 deletions.
33 changes: 21 additions & 12 deletions examples/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A basic example that connects two packet generators to a network wire with
a propagation delay distribution, and then to a packet sink.
"""

from functools import partial
import random
from random import expovariate
Expand All @@ -13,12 +14,12 @@


def arrival_1():
""" Packets arrive with a constant interval of 1.5 seconds. """
"""Packets arrive with a constant interval of 1.5 seconds."""
return 1.5


def arrival_2():
""" Packets arrive with a constant interval of 2.0 seconds. """
"""Packets arrive with a constant interval of 2.0 seconds."""
return 2.0


Expand Down Expand Up @@ -47,13 +48,21 @@ def packet_size():

env.run(until=100)

print("Flow 1 packet delays: " +
", ".join(["{:.2f}".format(x) for x in ps.waits['flow_1']]))
print("Flow 2 packet delays: " +
", ".join(["{:.2f}".format(x) for x in ps.waits['flow_2']]))

print("Packet arrival times in flow 1: " +
", ".join(["{:.2f}".format(x) for x in ps.arrivals['flow_1']]))

print("Packet arrival times in flow 2: " +
", ".join(["{:.2f}".format(x) for x in ps.arrivals['flow_2']]))
print(
"Flow 1 packet delays: "
+ ", ".join(["{:.2f}".format(x) for x in ps.waits["flow_1"]])
)
print(
"Flow 2 packet delays: "
+ ", ".join(["{:.2f}".format(x) for x in ps.waits["flow_2"]])
)

print(
"Packet arrival times in flow 1: "
+ ", ".join(["{:.2f}".format(x) for x in ps.arrivals["flow_1"]])
)

print(
"Packet arrival times in flow 2: "
+ ", ".join(["{:.2f}".format(x) for x in ps.arrivals["flow_2"]])
)
11 changes: 8 additions & 3 deletions examples/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
A basic example that showcases how TCP can be used to generate packets, and how a TCP sink
can send acknowledgment packets back to the sender in a simple two-hop network.
"""

import simpy

from ns.flow.cc import TCPReno
from ns.flow.cubic import TCPCubic
from ns.flow.flow import AppType, Flow
from ns.packet.tcp_generator import TCPPacketGenerator
from ns.packet.tcp_sink import TCPSink
from ns.port.wire import Wire
from ns.switch.switch import SimplePacketSwitch
from ns.flow.flow import AppType, Flow
from ns.flow.cubic import TCPCubic


def packet_arrival():
Expand Down Expand Up @@ -37,7 +40,9 @@ def delay_dist():
size_dist=packet_size,
)

sender = TCPPacketGenerator(env, flow=flow, cc=TCPCubic(), rtt_estimate=0.5, debug=True)
sender = TCPPacketGenerator(
env, flow=flow, cc=TCPReno(), element_id=flow.src, debug=True
)

wire1_downstream = Wire(env, delay_dist)
wire1_upstream = Wire(env, delay_dist)
Expand Down
22 changes: 17 additions & 5 deletions ns/packet/dist_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
the packets generated. The DistPacketGenerator's `out` member variable is used to connect the
generator to any network element with a `put()` member function.
"""

from ns.packet.packet import Packet


Expand Down Expand Up @@ -37,7 +38,8 @@ def __init__(
arrival_dist,
size_dist,
initial_delay=0,
finish=float("inf"),
finish=None,
size=None,
flow_id=0,
rec_flow=False,
debug=False,
Expand All @@ -47,9 +49,11 @@ def __init__(
self.arrival_dist = arrival_dist
self.size_dist = size_dist
self.initial_delay = initial_delay
self.finish = finish
self.finish = float("inf") if finish == None else finish
self.size = float("inf") if size == None else size
self.out = None
self.packets_sent = 0
self.sent_size = 0
self.action = env.process(self.run())
self.flow_id = flow_id

Expand All @@ -61,7 +65,8 @@ def __init__(
def run(self):
"""The generator function used in simulations."""
yield self.env.timeout(self.initial_delay)
while self.env.now < self.finish:

while self.env.now < self.finish and self.sent_size < self.size:
packet = Packet(
self.env.now,
self.size_dist(),
Expand All @@ -75,13 +80,20 @@ def run(self):
self.out.put(packet)

self.packets_sent += 1
self.sent_size += packet.size

if self.rec_flow:
self.time_rec.append(packet.time)
self.size_rec.append(packet.size)

if self.debug:
print(
f"Sent packet {packet.packet_id} with flow_id {packet.flow_id} at "
f"time {self.env.now}."
"DistPacketGenerator {} sent packet {:d} with size {:d}, "
"flow_id {:d} at time {:.4f}.".format(
self.element_id,
packet.packet_id,
packet.size,
packet.flow_id,
self.env.now,
)
)
75 changes: 40 additions & 35 deletions ns/packet/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,45 @@

class Packet:
"""
Packets in ns.py are generally created by packet generators, and will
run through a queue at an output port.
Packets in ns.py are generally created by packet generators, and will
run through a queue at an output port.
Key fields include: generation time, size, flow_id, packet id, source,
and destination. We do not model upper layer protocols, i.e., packets
don't contain a payload. The size (in bytes) field is used to determine
its transmission time.
Key fields include: generation time, size, flow_id, packet id, source,
and destination. We do not model upper layer protocols, i.e., packets
don't contain a payload. The size (in bytes) field is used to determine
its transmission time.
We use a float to represent the size of the packet in bytes so that we
can compare to ideal M/M/1 queues.
We use a float to represent the size of the packet in bytes so that we
can compare to ideal M/M/1 queues.
Parameters
----------
time: float
the time when the packet is generated.
size: float
the size of the packet in bytes
packet_id: int
an identifier for the packet
src, dst: int
identifiers for the source and destination
flow_id: int or str
an integer or string that can be used to identify a flow
Parameters
----------
time: float
the time when the packet is generated.
size: float
the size of the packet in bytes
packet_id: int
an identifier for the packet
src, dst: int
identifiers for the source and destination
flow_id: int or str
an integer or string that can be used to identify a flow
"""
def __init__(self,
time,
size,
packet_id,
realtime=0,
last_ack_time=0,
delivered=-1,
src="source",
dst="destination",
flow_id=0,
payload=None,
tx_in_flight=-1):

def __init__(
self,
time,
size,
packet_id,
realtime=0,
last_ack_time=0,
delivered=-1,
src="source",
dst="destination",
flow_id=0,
payload=None,
tx_in_flight=-1,
):
self.time = time
self.delivered_time = last_ack_time
self.first_sent_time = 0
Expand All @@ -55,13 +58,15 @@ def __init__(self,
self.lost = 0
self.self_lost = False
self.tx_in_flight = tx_in_flight
if(delivered == -1) : self.delivered = packet_id
else: self.delivered = delivered
if delivered == -1:
self.delivered = packet_id
else:
self.delivered = delivered

self.is_app_limited = False
self.color = None # Used by the two-rate tri-color token bucket shaper
self.prio = {} # used by the Static Priority scheduler
self.ack = 0 # used by TCPPacketGenerator and TCPSink
self.ack = None # used by TCPPacketGenerator and TCPSink
self.current_time = 0 # used by the Wire element
self.perhop_time = {} # used by Port to record per-hop arrival times

Expand Down
71 changes: 23 additions & 48 deletions ns/packet/tcp_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,9 @@ def run(self):

if self.debug:
print(
"TCPPacketGenerator {:d} sent packet {:d} with size {:d}, "
"flow_id {:d} at time {:.4f}.".format(
self.element_id,
packet.packet_id,
packet.size,
packet.flow_id,
self.env.now,
)
f"TCPPacketGenerator {self.element_id} sent packet {packet.packet_id} "
f"with size {packet.size}, flow_id {packet.flow_id} at "
f"time {self.env.now:.4f}."
)

self.out.put(packet)
Expand All @@ -127,10 +122,8 @@ def run(self):

if self.debug:
print(
" TCPPacketGenerator {:d} is setting a timer for packet {:d} with an RTO"
" of {:.4f}.".format(
self.element_id, packet.packet_id, self.rto
)
f"TCPPacketGenerator {self.element_id} is setting a timer "
f"for packet {packet.packet_id} with an RTO of {self.rto:.4f}."
)
else:
# No further space in the congestion window to transmit packets
Expand All @@ -141,9 +134,8 @@ def timeout_callback(self, packet_id=0):
"""To be called when a timer expired for a packet with 'packet_id'."""
if self.debug:
print(
"TCPPacketGenerator {:d}'s Timer expired for packet {:d} at time {:.4f}.".format(
self.element_id, packet_id, self.env.now
)
f"TCPPacketGenerator {self.element_id}'s Timer expired for packet "
"{packet_id} at time {self.env.now:.4f}."
)

self.congestion_control.timer_expired()
Expand All @@ -154,12 +146,8 @@ def timeout_callback(self, packet_id=0):

if self.debug:
print(
"TCPPacketGenerator {:d} is resending packet {:d} with flow_id {:d} at time {:.4f}.".format(
self.element_id,
resent_pkt.packet_id,
resent_pkt.flow_id,
self.env.now,
)
f"TCPPacketGenerator {self.element_id} is resending packet {resent_pkt.packet_id} "
f"with flow_id {resent_pkt.flow_id} at time {self.env.now:.4f}."
)

# starting a new timer for this segment and doubling the retransmission timeout
Expand All @@ -186,12 +174,9 @@ def put(self, ack):
resent_pkt.time = self.env.now
if self.debug:
print(
"TCPPacketGenerator {:d} is resending packet {:d} with flow_id {:d} at time {:.4f}.".format(
self.element_id,
resent_pkt.packet_id,
resent_pkt.flow_id,
self.env.now,
)
f"TCPPacketGenerator {self.element_id} is resending packet "
f"{resent_pkt.packet_id} with flow_id {resent_pkt.flow_id} at time "
f"{self.env.now:.4f}."
)

self.out.put(resent_pkt)
Expand All @@ -212,14 +197,9 @@ def put(self, ack):

if self.debug:
print(
"TCPPacketGenerator {:d} sent packet {:d} with size {:d}, "
"flow_id {:d} at time {:.4f} as dupack > 3.".format(
self.element_id,
packet.packet_id,
packet.size,
packet.flow_id,
self.env.now,
)
f"TCPPacketGenerator {self.element_id} sent packet "
f"{packet.packet_id} with size {packet.size}, flow_id "
f"{packet.flow_id} at time {self.env.now:.4f} as dupack > 3."
)

self.out.put(packet)
Expand All @@ -234,10 +214,8 @@ def put(self, ack):

if self.debug:
print(
"TCPPacketGenerator {:d} is setting a timer for packet {:d} with an RTO"
" of {:.4f}.".format(
self.element_id, packet.packet_id, self.rto
)
f"TCPPacketGenerator {self.element_id} is setting a timer for "
f"packet {packet.packet_id} with an RTO of {self.rto:.4f}."
)

return
Expand Down Expand Up @@ -280,14 +258,12 @@ def put(self, ack):

if self.debug:
print(
"TCPPacketGenerator {:d} received Ack till sequence number {:d} at time {:.4f}.".format(
self.element_id, ack.ack, self.env.now
)
f"TCPPacketGenerator {self.element_id} received ack till sequence number "
f"{ack.ack} at time {self.env.now:.4f}."
)
print(
"TCPPacketGenerator {:d} congestion window size = {:.1f}, last ack = {:d}.".format(
self.element_id, self.congestion_control.cwnd, self.last_ack
)
f"TCPPacketGenerator {self.element_id} congestion window size = "
f"{self.congestion_control.cwnd:.1f}, last ack = {self.last_ack}."
)

# this acknowledgment should acknowledge all the intermediate
Expand All @@ -301,9 +277,8 @@ def put(self, ack):
for packet_id in acked_packets:
if self.debug:
print(
"TCPPacketGenerator {:d} stopped timer {:d} at time {:.4f}.".format(
self.element_id, packet_id, self.env.now
)
f"TCPPacketGenerator {self.element_id} stopped timer "
f"{packet_id} at time {self.env.now:.4f}."
)
self.timers[packet_id].stop()
del self.timers[packet_id]
Expand Down

0 comments on commit 58fe750

Please sign in to comment.