计算机网络——实现Go-Back-N

实验目的

运用各种编程语言实现基于 Go-Back-N 的可靠数据传输软件。

实验环境

系统:Mac OS X 10.13.1
语言:Java
开发平台:eclipse
内部库:java.net,java.util

实验过程

代码设计

为了完成本次试验的要求,我编写了两个Java代码文件,分别实现server端功能和client端功能:

  1. server端代码流程
    主要算法流程如下所示,server端实现在一定丢包率范围内接收数据包并且回传ACK:
    Alt text
  2. client端代码流程
    主要的算法流程如下,client端主要实现数据包的传送以及延迟处理:
    Alt text

主机ip获取

在启用Go-Back-N之前,我们首先需要获得个人电脑的IP。在Mac OS X系统,使用终端命令ifconfig即可直接查看主机IP,具体如下:
Alt text

丢包率为0下的测试

为了验证代码的正确性,我首先先把丢包率设置为0查看了具体的传输流程,代码执行结果如下:

窗口长度 windowSize = 4;
数据包总数 num = 10;
超时限制 Timer = 1000ms

执行结果如下:
Alt text
Alt text

丢包率为10%下的测试

为了继续验证代码的正确性,而后我把丢包率设置为10%查看了具体的传输流程,代码执行结果如下:
Alt text
Alt text

小结

本次实验中,我们完成了java的网络编程,模拟了GBN算法的运行,对GBN协议有了进一步的了解。
在GBN协议中,接收方会丢弃所有失序的分组,尽管丢弃一个正确接收但是失序的分组有点浪费。但是根据GBN的原理。假定当前期望的编号是n,但是n+1提前达到。因为数据必须是有序交付,接收方可能缓存分组n+1,然后在他收到并交付分组n后,再将该分组交付到上层。但是如果n丢失,则n和n+1的分组都会被重发,因此,接收方只需要丢弃分组n+1即可。
这种方法的优点是接收方缓存简单,即接收方不需要缓存任何失序分组。对于发送方来说,他只需要维护窗口的下边界和和nextseqnum在窗口中的位置。对于接收方来说,只需要维护下一个按序接收的分组的序号。

代码附录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/************************发送方************************/
public class Client {
//定义流量窗格长度,起始和终止数据包号,计时器
public static int windowSize;
public static int start=0, end, num;
public static long timerTime = 0;

public static void main(String[] args) throws Exception {
//建立连接
InetAddress serverAddress = InetAddress.getByName("192.168.1.106");
DatagramSocket clientSocket = new DatagramSocket(9999);
byte[] sendData;
Timer timer = new Timer();
timer.schedule(new DelayActionListener(clientSocket), 0L, 500L);

//通过输入初始化参数
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter number of packets: ");
num = scanner.nextInt();
System.out.println("Please enter slide window size: ");
windowSize = scanner.nextInt();
System.out.println();
end = start + windowSize;

//启动计时
Timer[] timers = new Timer[num];
long startTime = System.currentTimeMillis();

//首先发送第一个流量窗内的数据包
System.out.println("<--Client send packets " + start + " - " + (start+windowSize-1));
for (int i=start;i<end;i++){
sendData = (i + "seq").getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, 8888);
clientSocket.send(sendPacket);
}

//循环发送剩余的数据包
while (true){
byte[] recvData = new byte[100];
DatagramPacket recvPacket = new DatagramPacket(recvData, recvData.length);
clientSocket.receive(recvPacket);
String in = new String(recvPacket.getData()).trim();
int ack_seq = Integer.parseInt(in.substring(3, in.length()));
System.out.println("-->Client received ACK: " + (ack_seq-1));
//正常情况
if (ack_seq >= start && ack_seq < end){
int oldEnd = end;
start = ack_seq;
end = ack_seq + windowSize;
if (end > num) end = num;
//发送数据包
for (int i = oldEnd; i < end; i++) {
sendData = (i + "seq").getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, 8888);
clientSocket.send(sendPacket);
System.out.println("<--Client sent packet "+i);
}
timerTime = System.currentTimeMillis();
}
//传到流量窗的右界限
if (ack_seq == end) timerTime = 0;
//传输结束
if (ack_seq == num){
timer.cancel();
System.out.println("Success sent all packets");
System.out.println("Time to send " + num + " packets successfully was " + (System.currentTimeMillis() - startTime) + "ms");
return;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/************************接收方************************/
public class Server {
public static void main(String[] args) throws Exception {
//创建套接字,初始化参数
DatagramSocket serverSocket = new DatagramSocket(8888);
int endReceive = 0;
int missedCtr = 0;
System.out.println("\n\n\nServer started.\n");

while (true){
byte[] data = new byte[100];
//接收数据包
DatagramPacket receivePacket = new DatagramPacket(data, data.length);
serverSocket.receive(receivePacket);
//截取seq
String in = new String(receivePacket.getData()).trim();
int seq = Integer.parseInt(in.substring(0, in.length() - 3));
System.out.println("Received packet " + seq+"<--");
//在一定丢包率的范围内操作
if (seq == 0 || Math.random()<=0.9){
//接收数据包并发送ACK
if (seq == endReceive){
endReceive++;
byte[] ackData = new String("ack"+endReceive).getBytes();
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
DatagramPacket sendPacket = new DatagramPacket(ackData, ackData.length, clientAddress, clientPort);
serverSocket.send(sendPacket);
System.out.println("Server sent ACK: " + seq+"-->");
}
//有数据包超前到达
else if (endReceive != 0){
if (seq == endReceive + 1) {
System.out.println("Packet lost or disordered on way to server: " + endReceive);
missedCtr++;
System.out.println("Missed: " + missedCtr);
}
byte[] ackData = new String("ack"+(endReceive)).getBytes();
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
DatagramPacket sendPacket = new DatagramPacket(ackData, ackData.length, clientAddress, clientPort);
serverSocket.send(sendPacket);
System.out.println("Server sent ACK: " + (endReceive-1)+"-->");
}
}
//有数据包丢失
else {
System.out.println("Drop packet " + seq);
}
}
}}
小手一抖⬇️