制作一个跟踪更多统计信息的UDP服务器/客户端程序

时间:2017-04-05 00:26:54

标签: java udp client-server sliding-window

我是编程服务器/客户端程序的新手。我刚刚学会了如何做停止等待,但我想尝试一下Go-Back-N。我制作了一个允许简单文件传输的程序,但我希望能够像我在Stop-and-Wait中那样从中获取更多信息:传输的数据包总数,发送的确认总数,收到的确认总数,收到的重复数据包总数,发送的数据总量和操作日志文件,记录客户端和服务器之间的所有活动。

我有一些更基本的统计数据,例如重传次数,传输时间和发送文件的通知,但这一次只适用于一个文件。我希望能够发送多个数据包。

到目前为止,这是我的代码:

接收器:

public class Receiver {

public static void main(String args[]) throws Exception {
    System.out.println("Ready to receive the file!");

    // Get the address, port and name of file to send over UDP
    final int port = Integer.parseInt(args[0]);
    final String fileName = args[1];

    receiveAndCreate(port, fileName);
}

public static void receiveAndCreate(int port, String fileName) throws IOException {
    // Create the socket, set the address and create the file to be sent
    DatagramSocket socket = new DatagramSocket(port);
    InetAddress address;
    File file = new File(fileName);
    FileOutputStream outToFile = new FileOutputStream(file);

    // Create a flag to indicate the last message
    boolean lastMessageFlag = false;

    // Store sequence number
    int sequenceNumber = 0;
    int lastSequenceNumber = 0;

    // For each message we will receive
    while (!lastMessageFlag) {
        // Create byte array for full message and another for file data without header
        byte[] message = new byte[1024];
        byte[] fileByteArray = new byte[1021];

        // Receive packet and retreive message
        DatagramPacket receivedPacket = new DatagramPacket(message, message.length);
        socket.setSoTimeout(0);
        socket.receive(receivedPacket);
        message = receivedPacket.getData();

        // Get port and address for sending ack
        address = receivedPacket.getAddress();
        port = receivedPacket.getPort();

        // Retrieve sequence number
        sequenceNumber = ((message[0] & 0xff) << 8) + (message[1] & 0xff);

        // Retrieve the last message flag
        if ((message[2] & 0xff) == 1) {
            lastMessageFlag = true;
        } else {
            lastMessageFlag = false;
        }

        if (sequenceNumber == (lastSequenceNumber + 1)) {

            // Update latest sequence number
            lastSequenceNumber = sequenceNumber;

            // Retrieve data from message
            for (int i=3; i < 1024 ; i++) {
                fileByteArray[i-3] = message[i];
            }

            // Write the message to the file
            outToFile.write(fileByteArray);
            System.out.println("Received: Sequence number = " + sequenceNumber +", Flag = " + lastMessageFlag);

            // Send acknowledgement
            sendAck(lastSequenceNumber, socket, address, port);

            // Check for last message
            if (lastMessageFlag) {
                outToFile.close();
            } 
        } else {
            // If packet has been received, send ack for that packet again
            if (sequenceNumber < (lastSequenceNumber + 1)) {
                // Send acknowledgement for received packet
                sendAck(sequenceNumber, socket, address, port);
            } else {
                // Resend acknowledgement for last packet received
                sendAck(lastSequenceNumber, socket, address, port);
            }
        }
    }

    socket.close();
    System.out.println("File " + fileName + " has been received.");


}
public static void sendAck(int lastSequenceNumber, DatagramSocket socket, InetAddress address, int port) throws IOException {
    // Resend acknowledgement
    byte[] ackPacket = new byte[2];
    ackPacket[0] = (byte)(lastSequenceNumber >> 8);
    ackPacket[1] = (byte)(lastSequenceNumber);
    DatagramPacket acknowledgement = new  DatagramPacket(ackPacket, ackPacket.length, address, port);
    socket.send(acknowledgement);
    System.out.println("Sent ack: Sequence Number = " + lastSequenceNumber);
}
}

发信人:

public class Sender {

    public static void main(String args[]) throws Exception {
        // Get the address, port and name of file to send over UDP
        final String hostName = args[0];
        final int port = Integer.parseInt(args[1]);
        final String fileName = args[2];

        createAndSend(hostName, port, fileName);
    }

    public static void createAndSend(String hostName, int port, String fileName) throws IOException {
        System.out.println("Sending the file");

        // Create the socket, set the address and create the file to be sent
        DatagramSocket socket = new DatagramSocket();
        InetAddress address = InetAddress.getByName(hostName);
        File file = new File(fileName); 

        // Create a byte array to store the filestream
        InputStream inFromFile = new FileInputStream(file);
        byte[] fileByteArray = new byte[(int)file.length()];
        inFromFile.read(fileByteArray);

        // Start timer for calculating throughput
        StartTime timer = new StartTime(0);

        // Create a flag to indicate the last message and a 16-bit sequence number
        int sequenceNumber = 0;
        boolean lastMessageFlag = false;

        // Create a flag to indicate the last acknowledged message and a 16-bit sequence number
        int ackSequenceNumber = 0;
        int lastAckedSequenceNumber = 0;
        boolean lastAcknowledgedFlag = false;

        // Create a counter to count number of retransmissions and initialize window size
        int retransmissionCounter = 0;
        int windowSize = 128;

        // Vector to store the sent messages
        Vector <byte[]> sentMessageList = new Vector <byte[]>();

        // For as each message we will create
        for (int i=0; i < fileByteArray.length; i = i+1021 ) {

            // Increment sequence number
            sequenceNumber += 1;

            // Create new byte array for message
            byte[] message = new byte[1024];

            // Set the first and second bytes of the message to the sequence number
            message[0] = (byte)(sequenceNumber >> 8);
            message[1] = (byte)(sequenceNumber);

            // Set flag to 1 if packet is last packet and store it in third byte of header
            if ((i+1021) >= fileByteArray.length) {
                lastMessageFlag = true;
                message[2] = (byte)(1);
            } else { // If not last message store flag as 0
                lastMessageFlag = false;
                message[2] = (byte)(0);
            }

            // Copy the bytes for the message to the message array
            if (!lastMessageFlag) {
                for (int j=0; j != 1021; j++) {
                    message[j+3] = fileByteArray[i+j];
                }
            }
            else if (lastMessageFlag) { // If it is the last message
                for (int j=0;  j < (fileByteArray.length - i); j++) {
                    message[j+3] = fileByteArray[i+j];
                }
            }

            // Package the message
            DatagramPacket sendPacket = new DatagramPacket(message, message.length, address, port);

            // Add the message to the sent message list
            sentMessageList.add(message);

            while (true) {
                // If next sequence number is outside the window
                if ((sequenceNumber - windowSize) > lastAckedSequenceNumber) {

                    boolean ackRecievedCorrect = false;
                    boolean ackPacketReceived = false;

                    while (!ackRecievedCorrect) {
                        // Check for an ack
                        byte[] ack = new byte[2];
                        DatagramPacket ackpack = new DatagramPacket(ack, ack.length);

                        try {
                            socket.setSoTimeout(50);
                            socket.receive(ackpack);
                            ackSequenceNumber = ((ack[0] & 0xff) << 8) + (ack[1] & 0xff);
                            ackPacketReceived = true;
                        } catch (SocketTimeoutException e) {
                            ackPacketReceived = false;
                            //System.out.println("Socket timed out while waiting for an acknowledgement");
                            //e.printStackTrace();
                        }

                        if (ackPacketReceived) {
                            if (ackSequenceNumber >= (lastAckedSequenceNumber + 1)) {
                                lastAckedSequenceNumber = ackSequenceNumber;
                            }
                            ackRecievedCorrect = true;
                            System.out.println("Ack recieved: Sequence Number = " + ackSequenceNumber);
                            break;  // Break if there is an ack so the next packet can be sent
                        } else { // Resend the packet
                            System.out.println("Resending: Sequence Number = " + sequenceNumber);
                            // Resend the packet following the last acknowledged packet and all following that (cumulative acknowledgement)
                            for (int y=0; y != (sequenceNumber - lastAckedSequenceNumber); y++) {
                                byte[] resendMessage = new byte[1024];
                                resendMessage = sentMessageList.get(y + lastAckedSequenceNumber);

                                DatagramPacket resendPacket = new DatagramPacket(resendMessage, resendMessage.length, address, port);
                                socket.send(resendPacket);
                                retransmissionCounter += 1;
                            }
                        }
                    }
                } else { // Else pipeline is not full, break so we can send the message
                    break;
                }
            }

            // Send the message
            socket.send(sendPacket);
            System.out.println("Sent: Sequence number = " + sequenceNumber + ", Flag = " + lastMessageFlag);


            // Check for acknowledgements
            while (true) {
                boolean ackPacketReceived = false;
                byte[] ack = new byte[2];
                DatagramPacket ackpack = new DatagramPacket(ack, ack.length);

                try {
                    socket.setSoTimeout(10);
                    socket.receive(ackpack);
                    ackSequenceNumber = ((ack[0] & 0xff) << 8) + (ack[1] & 0xff);
                    ackPacketReceived = true;
                } catch (SocketTimeoutException e) {
                    //System.out.println("Socket timed out waiting for an ack");
                    ackPacketReceived = false;
                    //e.printStackTrace();
                    break;
                }

                // Note any acknowledgements and move window forward
                if (ackPacketReceived) {
                    if (ackSequenceNumber >= (lastAckedSequenceNumber + 1)) {
                        lastAckedSequenceNumber = ackSequenceNumber;
                        System.out.println("Ack recieved: Sequence number = " + ackSequenceNumber);
                    }
                }
            }
        }

        // Continue to check and resend until we receive final ack
        while (!lastAcknowledgedFlag) {

            boolean ackRecievedCorrect = false;
            boolean ackPacketReceived = false;

            while (!ackRecievedCorrect) {
                // Check for an ack
                byte[] ack = new byte[2];
                DatagramPacket ackpack = new DatagramPacket(ack, ack.length);

                try {
                    socket.setSoTimeout(50);
                    socket.receive(ackpack);
                    ackSequenceNumber = ((ack[0] & 0xff) << 8) + (ack[1] & 0xff);
                    ackPacketReceived = true;
                } catch (SocketTimeoutException e) {
                    //System.out.println("Socket timed out waiting for an ack1");
                    ackPacketReceived = false;
                    //e.printStackTrace();
                }

                // If its the last packet
                if (lastMessageFlag) {
                    lastAcknowledgedFlag = true;
                    break;
                } 
                // Break if we receive acknowledgement so that we can send next packet
                    if (ackPacketReceived) {  
                    System.out.println("Ack recieved: Sequence number = " + ackSequenceNumber);
                    if (ackSequenceNumber >= (lastAckedSequenceNumber + 1)) {
                        lastAckedSequenceNumber = ackSequenceNumber;
                    }
                    ackRecievedCorrect = true;
                    break; // Break if there is an ack so the next packet can be sent
                } else { // Resend the packet
                    // Resend the packet following the last acknowledged packet and all following that (cumulative acknowledgement)
                    for (int j=0; j != (sequenceNumber-lastAckedSequenceNumber); j++) {
                        byte[] resendMessage = new byte[1024];
                        resendMessage = sentMessageList.get(j + lastAckedSequenceNumber);
                        DatagramPacket resendPacket = new DatagramPacket(resendMessage, resendMessage.length, address, port);
                        socket.send(resendPacket);
                        System.out.println("Resending: Sequence Number = " + lastAckedSequenceNumber);

                        // Increment retransmission counter
                        retransmissionCounter += 1;
                    }
                }
            }
        }

        socket.close();
        System.out.println("File " + fileName + " has been sent");

        // Calculate the average throughput
        int fileSizeKB = (fileByteArray.length) / 1024;
        int transferTime = timer.getTimeElapsed() / 1000;
        double throughput = (double) fileSizeKB / transferTime;
        System.out.println("File size: " + fileSizeKB + "KB, Transfer time: " + transferTime + " seconds. Throughput: " + throughput + "KBps");
        System.out.println("Number of retransmissions: " + retransmissionCounter); 
    }
}

我想我的主要问题是将它从仅接受一个文件转换为多个文件?

提前谢谢!

0 个答案:

没有答案