DataLink 模拟器






4.88/5 (17投票s)
使用 OpenGL 的 C# 数据链路层模拟

引言
数据链路层是计算机网络七层 OSI 模型的第二层。它对应于或属于链接层,是TCP/IP 参考模型的一部分。
数据链路层提供在网络实体之间传输数据的功能和程序手段,并且可能提供检测和可能纠正可能发生在物理层的错误的方法。
这个 C# 模拟将涵盖数据链路层的流量控制技术,包括
- 停止等待
- 后退 N 帧
- 选择重传
停止等待
停止等待 ARQ 是最简单的自动重传请求 (ARQ) 方法。停止等待 ARQ 发送方一次发送一帧。发送每一帧后,发送方在收到 ACK(确认)信号之前不会发送任何其他帧。在收到良好帧后,接收方发送 ACK。如果 ACK 在特定时间(称为超时)之前没有到达发送方,则发送方将再次发送同一帧。
现在我们有 3 种情况
- 帧到达接收方 Rx,确认到达发送方 Tx。
- 帧超时。
- 帧到达接收方 Rx,确认超时。
在最后一种情况下,发送方没有收到 ACK,超时,然后再次发送帧。现在接收方有两个相同帧的副本,并且不知道第二个帧是重复帧还是携带相同数据的序列中的下一个帧。
(为了简单起见,如果 Rx 已经有该帧,则忽略重复帧。)

Using the Code
objselected_frame_stp_wt
是我们从发送方列表框
中选择的帧dtimeout
是我们预定的超时时间tsSend_Time
是一个由计时器计算的时间段对象objFrame
和objAck
是来自clssSqaure
类的对象,负责创建和移动图元
if (objselected_frame_stp_wt != null)
{
if (tsSend_Time.Seconds > dtimeout)//send time exceeded predetermined timeout
{
if (bsend_file) //Rx didn't receive Frame
lbl_receiver.Text = "Timeout";
else //Tx didn't receive Acknowledgment
lbl_sender.Text = "Timeout";
timer1.Enabled = false;//stop sending
}
if (bsend_file)//Frame is being sent to Rx
{
if (LBx_Receiver.Items.Contains(objselected_frame_stp_wt))
lbl_sender.Text = "Resending " + objselected_frame_stp_wt.ToString();
else
lbl_sender.Text = "Sending " + objselected_frame_stp_wt.ToString();
if ((objFrame.Fxpos + dlen) > nwidth)//frame arrived to Rx
{
if (!LBx_Receiver.Items.Contains(objselected_frame_stp_wt))
{
LBx_Receiver.Items.Add(objselected_frame_stp_wt);
}
lbl_sender.Text = "awaiting acknowledgement";
lbl_receiver.Text = "sending acknowledgement";
bsend_file = false;
dtstart = DateTime.Now;
objAck = new clssSqaure(nwidth, -20, -1, color_yellow);
lst_clssSqaure.Add(objAck);
}
objFrame.Fxpos += fspeed * objFrame.Fvec_X;
}
else//Ack is being sent to Tx
{
if (((objAck.Fxpos - dlen + 10) < (nwidth * -1)))//Ack arrived to Tx
{
lbl_sender.Text = "Acknowledgement received";
if (LBx_Receiver.Items.Contains(objselected_frame_stp_wt))
{
LBx_Sender.Items.Remove(objselected_frame_stp_wt);
}
timer1.Enabled = false;
bsend_file = true;
}
objAck.Fxpos += fspeed * objAck.Fvec_X;
}
}
与其他 ARQ 相比,停止等待 ARQ 的效率较低,因为数据包之间的间隔(如果 ACK 和数据成功接收)是传输时间的两倍。
为了解决这个问题,人们可以一次发送多个数据包,使用更大的序列号,并为一组数据包使用一个 ACK。这在后退 N 帧 ARQ 和选择重传 ARQ 中是这样做的。
后退 N 帧
发送过程继续发送由窗口大小指定的一定数量的帧,而无需从接收方接收 ACK 数据包。
如果任何帧丢失或损坏,或者确认它们的 ACK 丢失或损坏,则该帧以及窗口中的所有后续帧(即使它们没有错误地收到)都将被重新发送。

Using the Code
滑动窗口是一个同时用于后退 N 帧和选择重传的函数。
Q_Frames
是一个保存窗口大小帧的队列,当一个帧在规定时间内成功到达Rx时,将创建一个objAck
并将其添加到队列Q_Acks
中。
如果一个帧被丢弃 (并且我们处于后退 N 帧模式),Rx 将停止接收。
private void SlidingWindow(TimeSpan tsSend_Time, bool bIsGoBackN)
{
object[] arr_Frames = Q_Frames.ToArray();
if (Q_Frames.Count != 0)//Sending Frames
{
objFrame.Fxpos += fspeed * objFrame.Fvec_X;
lbl_sender.Text = "Sending " + arr_Frames[0].ToString();
lbl_receiver.Text = "Receiving " + arr_Frames[0].ToString();
if (objFrame.Fxpos + dlen > nwidth)//frame reached Rx
{
object Objreceivedframe = Q_Frames.Dequeue();
if (tsSend_Time.Seconds < dtimeout) //within time
{
if (bcontinue_receive)//sending was not interrupted,
//receive frames normally
{
LBx_Receiver.Items.Add(Objreceivedframe);
LBx_Log.Items.Add(Objreceivedframe.ToString() + " received");
Q_Acks.Enqueue(Objreceivedframe);
objAck = new clssSqaure(nwidth, -20, -1,
color_yellow);//Send Ack
lst_clssSqaure.Add(objAck);
}
}
else//frame exceeded time
{
LBx_Log.Items.Add(Objreceivedframe.ToString() + " timed out");
if (bIsGoBackN && bcontinue_receive)
{
LBx_Log.Items.Add("Rx stopped receiving");
bcontinue_receive = false;//stop receiving
}
}
dtstart = DateTime.Now; //reset time
objFrame.Fxpos = 10 - nwidth; //reset Frame position
}
}
if (Q_Acks.Count != 0)//Sending Ack
{
objAck.Fxpos += fspeed * 2 * objAck.Fvec_X;
if (((objAck.Fxpos - dlen + 10) < (nwidth * -1)))//Ack arrived to Tx
{
LBx_Log.Items.Add("Ack received");
LBx_Sender.Items.Remove(Q_Acks.Dequeue());
lst_clssSqaure.Remove(objAck);
}
}
if (Q_Frames.Count == 0 && Q_Acks.Count == 0)
{
lbl_receiver.Text = lbl_sender.Text = "Ready";
lbl_TimeElapsed.Text = string.Empty;
}
}
选择重传
即使在帧丢失之后,发送过程也会继续发送由窗口大小指定的帧数。
与后退 N 帧 ARQ 不同,接收过程将继续接受和确认在初始错误之后发送的帧。如果来自发送方的帧未到达接收方,则发送方将继续发送后续帧,直到其窗口为空。
接收方继续使用后续帧填充其接收窗口,每次回复一个 ACK,其中包含最早丢失帧的序列号。一旦发送方发送了其窗口中的所有帧,它将重新发送由 ACK 给出的帧号,然后从中断的地方继续。

历史
- 2010年2月7日:初始帖子