Java 可靠的UDP算法实现

Java 可靠的UDP算法实现,java,Java,这是我第一次在这个网站上问一些问题。如果有什么错误,请对我温柔一点 我正在尝试使用UDP编写“可靠”的文件传输。 其思想是服务器将文件拆分为多个部分。每个部分包含许多数据包。 服务器尝试在一个部分发送所有数据包,并等待客户端响应。 客户端接收数据包并检查是否有丢失的数据包。如果是,则向服务器发送响应,并告知丢失的数据包。 服务器从客户端接收响应并重新发送丢失的数据包。 如果没有数据包丢失,服务器将切换到下一部分并继续,直到结束 这就是想法,这是实际的代码 public void Send() t

这是我第一次在这个网站上问一些问题。如果有什么错误,请对我温柔一点

我正在尝试使用UDP编写“可靠”的文件传输。 其思想是服务器将文件拆分为多个部分。每个部分包含许多数据包。
服务器尝试在一个部分发送所有数据包,并等待客户端响应。
客户端接收数据包并检查是否有丢失的数据包。如果是,则向服务器发送响应,并告知丢失的数据包。
服务器从客户端接收响应并重新发送丢失的数据包。
如果没有数据包丢失,服务器将切换到下一部分并继续,直到结束

这就是想法,这是实际的代码

public void Send() throws IOException, InterruptedException, ClassNotFoundException {

    DatagramSocket ds = new DatagramSocket(4567);
    File file = new File("D:\\DOWNLOAD\\HC.flac");
    FileInputStream f = new FileInputStream(file);

    byte[] b = new byte[10000];
    //calculate file length
    int remainLength = (int) file.length();

    //calculate total packet will be sent
    ByteBuffer bbuf = ByteBuffer.allocate(4);
    int totalPacket = remainLength / 10000 + 1;
    bbuf.putInt(totalPacket);
    byte[] result = bbuf.array();
    //send total packet to client
    DatagramPacket total_Packet = new DatagramPacket(result,result.length,InetAddress.getByName("192.168.1.5"), 4567);
    ds.send(total_Packet);

    //position of packet is being sent (in total packets)
    int packetNumber = 0;

    TimeUnit.MILLISECONDS.sleep(1);
    try {

        while (remainLength != 0) {
            //create an array of packet, the client has a same array
            //this array is in order to make all receive packet (on client) is in order and check if there is any lost packet
            PacketSend[] windowBuffer = new PacketSend[1000];
            //position of packet is being sent (in windowBuffer) 
            int windowIndex = 0;

            //this while loop assign packet into windowBuffer
            while (remainLength >= 10000) {
                //read file
                b = new byte[10000];
                f.read(b, 0, 10000);
                //create packet with its position
                PacketSend fs = new PacketSend(packetNumber,windowIndex, b);
                //assign packet into window
                windowBuffer[windowIndex] = fs;

                //subtract the remaining length of file by 10000 (1 packet hold 10000 byte data of the file)
                remainLength = remainLength - 10000;

                packetNumber++;
                //if windowIndex = 999, that means the windowBuffer hold full of packet, ready to send to client
                if (windowIndex == 999)
                    break;
                windowIndex++;

            }
            //if this is the last packet
            if (remainLength > 0 && remainLength < 10000) {
                //only read the remaining
                b = new byte[remainLength];
                int read = f.read(b, 0, remainLength);
                PacketSend fs = new PacketSend(packetNumber,windowIndex, b);
                windowBuffer[windowIndex] = fs;

                remainLength = 0;
            }

            //this for loop send all the packet in windowBuffer to client
            for (int j = 0; j <= windowIndex; j++) {
                //serialize packet before send
                ByteArrayOutputStream bStream = new ByteArrayOutputStream(
                        10076);
                ObjectOutput oos = new ObjectOutputStream(bStream);
                oos.writeObject(windowBuffer[j]);
                oos.close();
                byte[] serialize = new byte[10076];
                serialize = bStream.toByteArray();

                //send packet to client
                DatagramPacket dp = new DatagramPacket(serialize,
                        serialize.length,
                        InetAddress.getByName("192.168.1.5"), 4567);
                System.out.println("Sending " + serialize.length
                        + " bytes to client, port 4567, packet: "
                        + windowBuffer[j].packetNumber);

                ds.send(dp);

                //remove this line make the program always fail (because to many packets lost and it is easier for you to know what problem I am asking)
                //if this line exist, the program sometimes fail, sometimes work and I dont know why
                TimeUnit.MILLISECONDS.sleep(1);
            }

            //ds.setSoTimeout(2500);
            //when all packets in windowBuffer are sent to client
            //this while loop handle the lost packets
            while(true){

            byte[] requestedPacket = new byte[10000];
            DatagramPacket dp = new DatagramPacket(requestedPacket,
                    requestedPacket.length);
            try{
                //the server stop and wait for response from client
                //sometimes it just stop at there
                //I know UDP is unreliable, so the response from client may get lost, but I made the client resend its response to make sure server received
                //but it sometimes just stuck forever
                //Here is the problem I am asking
            ds.receive(dp);
            }
            catch (SocketTimeoutException ste){
                ds.close();
                break;
            }
            //deserialize the response from client
            ObjectInputStream iStream = new ObjectInputStream(
                    new ByteArrayInputStream(requestedPacket));
            ArrayList<Integer> missingPacket = new ArrayList<Integer>();
            //The response from client is an array of lost packets position (in windowBuffer)
            missingPacket = (ArrayList<Integer>) iStream.readObject();

            //if there is no lost packet, break this while loop
            //it mean move the windowBuffer to the next part of the file
            //the client move its windowBuffer too
            if (missingPacket.size() == 0){
                break;
            }


            System.out.printf("Resending %d missing packet.\n",missingPacket.size());
            //resend those lost packets
            //all the packets is still hold in windowBuffer, if you know which packet is missing, just resend them.
            for (int i = 0; i < missingPacket.size(); i++){
                //serialize
                ByteArrayOutputStream bStream = new ByteArrayOutputStream(
                        10076);
                ObjectOutput oos = new ObjectOutputStream(bStream);
                oos.writeObject(windowBuffer[missingPacket.get(i)]);
                oos.close();
                byte[] serialize = new byte[10076];
                serialize = bStream.toByteArray();

                //and send
                DatagramPacket dtp = new DatagramPacket(serialize,
                        serialize.length,
                        InetAddress.getByName("192.168.1.5"), 4567);
                System.out.println("Sending " + serialize.length
                        + " bytes to client, port 4567, packet: "
                        + windowBuffer[missingPacket.get(i)].packetNumber);
                ds.send(dtp);
                TimeUnit.MILLISECONDS.sleep(1);
            }

            }

            //--------------------------------------------
            //ket thuc xu ly mat goi tin
        }
        System.out.println("File Transfer completed !!");
    } catch (FileNotFoundException ex) {
        System.out.println("File not found :P");
    }

    finally {
        f.close();

    }
}
但有时程序可以运行,有时会失败。它在服务器停止并等待客户端响应的线路上失败。客户端尝试将响应重新发送到服务器,但服务器似乎没有收到任何响应

当我尝试使用这些代码来制作多线程GUI程序时,问题会变得更糟。它每次都卡在那条线上

我在real machine中运行服务器,在vmware中运行客户端,两者都连接到real LAN路由器

如果有人知道问题的原因,请告诉我。尽可能简单地解释,因为我不会说英语


多谢各位

为什么要使用UDP?为什么不是TCP?TCP通过内置ACK提供可靠的传递。不清楚您为什么要这样做,而不仅仅是使用TCP。您可能有XY问题。请详细说明。我知道TCP更好,但使用UDP是一项要求。任何人都想知道我为什么不使用TCP。因为这是深入理解TCP工作原理的一个家庭作业。实际上,我尝试用“更简单”的方法模拟选择性重复滑动窗口协议(我认为)。我尝试使滑动窗口只在固定位置移动,而不是像选择性重复滑动窗口协议那样移动。当它失败时,您能添加准确的错误消息吗?此外,您是否创建了它始终失败或通过的场景?你能描述这样的场景吗(在哪里失败,在哪里通过)?
    public void Receive() throws IOException, ClassNotFoundException, InterruptedException {

    DatagramSocket ds = new DatagramSocket(4567);
    try {

        byte[] receiveData = new byte[10103];
        byte[] receiveNumPacket = new byte[4];
        FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\TESTED\\Desktop\\abc\\test.mp3" + ""));
        {
            ds.setSoTimeout(10000);
            //receive total packets number from server
            DatagramPacket p = new DatagramPacket(receiveNumPacket, receiveNumPacket.length);
            ds.receive(p);

        }
        //calculate number of total packet from byte array
        ByteBuffer bb = ByteBuffer.wrap(receiveNumPacket);
        int totalPacket = bb.getInt();

        int windowSize = 1000;

        //set the timeout
        //if the timeout passed, it mean the server sent all its packet in windowBuffer and waiting for response
        ds.setSoTimeout(500);

        Outer: while (totalPacket > 0) {

            PacketSend[] windowBuffer = new PacketSend[1000];

            Inner: while (true) {
                try {

                    receiveData = new byte[10103];
                    DatagramPacket dp = new DatagramPacket(receiveData, receiveData.length);
                    //receive data from server
                    //if this packet lost, the program should fail. This is not the problem I'm asking
                    ds.receive(dp);
                    byte[] b1 = new byte[dp.getLength()];

                    //deserialize
                    ObjectInputStream iStream = new ObjectInputStream(new ByteArrayInputStream(receiveData));
                    PacketSend fr = new PacketSend();

                    //assign the receive packet to windowBuffer
                    fr = (PacketSend) iStream.readObject();
                    windowBuffer[fr.bufferIndex] = fr;
                    iStream.close();

                    System.out.printf("Receive packet number: %d \n", fr.packetNumber);

                    //timeout passed, it mean the server sent all its packet in windowBuffer and waiting for response
                } catch (SocketTimeoutException ste) {

                    while (true) {
                        //check if this is the last part of the file
                        if (totalPacket < 1000)
                            windowSize = totalPacket;

                        //check if there is any packet lost
                        //those lost packets position will be hold in missingPacket array
                        ArrayList<Integer> missingPacket = new ArrayList<Integer>();
                        for (int i = 0; i < windowSize; i++) {
                            if (windowBuffer[i] == null) {
                                missingPacket.add(i);
                            }

                        }
                        //if there is any lost packet
                        if (missingPacket.size() != 0) {

                            //serialize missingPacket array
                            ByteArrayOutputStream bStream = new ByteArrayOutputStream(10000);
                            ObjectOutput oos = new ObjectOutputStream(bStream);
                            oos.writeObject(missingPacket);
                            oos.close();
                            byte[] serialize = new byte[10000];
                            serialize = bStream.toByteArray();

                            DatagramPacket dp = new DatagramPacket(serialize, serialize.length,
                                    InetAddress.getByName("192.168.1.2"), 4567);
                            System.out.println("Request missing packet " + serialize.length
                                    + " bytes to server, port 4567, number of lost packets: "
                                    + missingPacket.size());

                            //send response to server, let it know which packet is lost
                            ds.send(dp);
                            TimeUnit.MILLISECONDS.sleep(1);
                            //continue inner loop
                            //it mean try to receive packet from server and do the check
                            continue Inner;

                        }
                        //if all packet in windowBuffer received

                        ByteArrayOutputStream bStream = new ByteArrayOutputStream(10000);
                        ObjectOutput oos = new ObjectOutputStream(bStream);
                        oos.writeObject(missingPacket);
                        oos.close();
                        byte[] serialize = new byte[10000];
                        serialize = bStream.toByteArray();

                        DatagramPacket dp = new DatagramPacket(serialize, serialize.length,
                                InetAddress.getByName("192.168.1.2"), 4567);
                        System.out.println("Notice server all packet received");
                        //send response to server
                        //but this time, the missingPacket array have no member
                        //this also the sign for the server know it's time for move its windowBuffer to new position
                        ds.send(dp);
                        totalPacket -= 1000;


                        //write file to disk
                        for (int i = 0; i < windowSize; i++) {
                            fos.write(windowBuffer[i].data, 0, windowBuffer[i].data.length);
                        }
                        //continue the outer loops
                        //it mean move windowBuffer to new position
                        continue Outer;
                    }
                }

            }
        }

        fos.close();
        System.out.println("File Transfer completed !!");
    } catch (SocketException ex) {
        System.out.println("UDP Port is occupied.");
        System.exit(1);
    }
}
public int packetNumber;
public int bufferIndex;
public byte[] data = new byte[1024];

public PacketSend(int _packetNumber,int _indexInBuffer,byte[] _data)
{
    packetNumber = _packetNumber;
    bufferIndex = _indexInBuffer;
    data = _data;
}
public PacketSend(){}
public PacketSend(PacketSend fs)
{
    packetNumber = fs.packetNumber;
    bufferIndex = fs.bufferIndex;
    data = fs.data;
}