编程实现可靠数据传输原理 Go-Back-N

发布时间 2023-04-25 20:31:16作者: Capterlliar

1. 编写接收端代码

接收端模拟网络环境较差时情况,每次生成一个随机数,小于0.8时不丢包,大于0.8时丢包。

接收数据格式:编号+空格+内容

返回数据格式:

丢包:Loss+空格+编号

未丢包:ACK+空格+编号

接收包非累计计数时不做处理。

2. 编写发送端代码

发送端较为复杂,分为两个线程:

发送线程:

设置窗口大小为5,当超过窗口时死循环,停止发送;

处理线程:

判断是否ACK,如果累计增加,窗口向右滑动一格,否则重发;

另设置20个定时器,如果超过5秒未响应,则重发。

理论上来说5个定时器就够了,但懒得改了(

Receiver代码:

import java.net.*;
public class Receiver {
    public static int send_port = 8899;
    public static int receive_port = 8848;
    public static void main(String[] args) throws Exception {
        InetAddress serverAddress = InetAddress.getByName("localhost");
        DatagramSocket socket = new DatagramSocket(receive_port);
        int cnt=0;
        while (true){
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            //此方法为阻塞方法
            socket.receive(packet);
            String r = new String(packet.getData(),0, packet.getLength());
            String[] result = r.split(" ",2);
            int num=Integer.parseInt(result[0]);
            if(Math.random()<0.8){
                //ack
                if(num==cnt){
                    String str = "ACK "+cnt;
                    byte[] res = str.getBytes();
                    DatagramPacket resp = new DatagramPacket(res, res.length,
                            serverAddress, send_port);
                    socket.send(resp);
                    System.out.println("receive result : " + result[1]);
                    cnt++;
                }
                else{
                    System.out.println("Discard packet "+num);
                }
            }
            else{
                //loss
                String str = "Loss "+num;
                byte[] res = str.getBytes();
                DatagramPacket resp = new DatagramPacket(res, res.length,
                        serverAddress, send_port);
                socket.send(resp);
                System.out.println("Loss : "+num);
            }

        }
    }
}
View Code

Client代码:

import javax.swing.Timer;
import java.io.IOException;
import java.net.*;

class Sender {
    //assume that the length of window is 5
    public static volatile int l=0;
    public static volatile int r=4;
    public static volatile int current=0;
    public static DatagramSocket socket;
    public static InetAddress serverAddress;
    public static int receive_port = 8848;
    public static Timer[] timers;
    public static String[] str;

    Sender(DatagramSocket s,InetAddress in){
        socket=s;
        serverAddress=in;
        str = new String[20];
        for(int i=0;i<20;i++){
            str[i]=i+" "+"Here is packet"+i;
        }
        timers = new Timer[20];
        for(int i=0;i<str.length;i++){
            int finalI = i;
            timers[i] = new Timer(5000, e -> {
                send(str[finalI]);
                System.out.println("Resend packet "+finalI);
                timers[finalI].restart();
            });
        }
    }

    public static void send(String s) {
        try {
            byte[] data = s.getBytes();
            DatagramPacket packet = new DatagramPacket(data, data.length,
                    serverAddress, receive_port);
            socket.send(packet);
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }

    }

    class SendPackage implements Runnable {
        @Override
        public void run(){
            for(int i=0;i<str.length;i++){
                send(str[i]);
                timers[i].start();
                current=i;
                while(current==r);
            }
        }
    }

    class DealMessage implements Runnable{

        @Override
        public void run() {
            while(l<str.length){
                byte[] rec = new byte[1024];
                DatagramPacket recp = new DatagramPacket(rec, rec.length);
                //此方法为阻塞方法
                try {
                    socket.receive(recp);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                String res = new String(recp.getData(),0, recp.getLength());
                System.out.println(res);
                String[] result=res.split(" ",2);
                int num=Integer.parseInt(result[1]);
                if(result[0].equals("ACK")){
                    if(num==l){
                        timers[num].stop();
                        l++;
                        System.out.println("Packet "+num+" has been received.");
                        r++;
                    }
                }
                else if(result[0].equals("Loss")){
                    System.out.println("Loss!Ready to send packet "+num+" again.");
                }
            }
        }
    }
}

public class Client {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(8899);
        InetAddress serverAddress = InetAddress.getByName("localhost");
        Sender sender = new Sender(socket,serverAddress);

        Sender.DealMessage dealMessage = sender.new DealMessage();
        Thread t1=new Thread(dealMessage);
        t1.start();
        Sender.SendPackage sendPackage = sender.new SendPackage();
        Thread t2=new Thread(sendPackage);
        t2.start();
    }
}
View Code