UDP 服务器从客户端获取数据,即使服务器是最后启动的

UDP server getting data from client even if server is started last

我目前正在为编程考试而学习,在较早的考试中,有一个问题具有以下代码。

public class Question18 {
public static class Question18Server implements Runnable {

    DatagramSocket serverSocket;
    byte[] receiveData = new byte[1024];
    byte[] sendData = new byte[1024];

    public Question18Server(int port) throws SocketException{
        serverSocket = new DatagramSocket(port);
    }
    public void run() {
        while (true) {
            DatagramPacket receivePacket = new
                    DatagramPacket(receiveData,
                    receiveData.length);
            try {
                serverSocket.receive(receivePacket);
                String sentence = new String(receivePacket.getData());
                System.out.println("RECEIVED: " + sentence);
            } catch (IOException e) {
            }
        }
    }
}
public static class Question18Client implements Runnable {
    int port;
    DatagramSocket clientSocket;
    InetAddress IPAddress;
    public Question18Client(int port) throws SocketException,
            UnknownHostException {
        this.port = port;
        clientSocket = new DatagramSocket();
        IPAddress = InetAddress.getByName("localhost");
    }
    public void run() {
        for (int i = 0; i < 4; i++) {
            byte[] sendData = new byte[1024];
            String sentence = "Hello " + i * 2;
            sendData = sentence.getBytes();
            DatagramPacket sendPacket = new DatagramPacket(sendData,
                    sendData.length, IPAddress, port);
            try {
                clientSocket.send(sendPacket);
            } catch (IOException e) {
            }
        }
    }
}
public static void main(String[] args) {
    try {

        Thread server = new Thread(new Question18Server(9000));
        Thread client = new Thread(new Question18Client(9000));

        server.start();
        client.start();

    } catch (Exception e) {
    }
  }
}

我遇到的问题是:

如果 server.start() 和 client.start() 取反会输出什么?

我对 UDP 没有太多经验,我可能误解了一些东西,但我最初的想法是服务器可能会收到客户端发送的一些数据报包。

但令我惊讶的是它收到了所有发送的数据。

我尝试将代码更改为以下代码以在两个线程的启动之间创建一个小暂停,但它仍然有效。

        client.start();            
        long waittime = System.currentTimeMillis() + 5000;
        while(System.currentTimeMillis() < waittime)
        {}            
        server.start();

我真的希望有人能解释为什么这是有效的,我目前的理论是

a) 由于我使用 "localhost"

,所以数据存储在我的电脑上

b) DatagramPackets 在一定时间内搜索正确的 IP 和端口。

由于 UDP 不需要连接设置,因此 publishersubscriber 的启动顺序不如 TCP 重要。在 UDP 中,迟到的订阅者不一定会看到所有数据包,即使订阅者没有迟到也无法保证。

在您的代码中,Question18Client发布者Question18Server订阅者。试试下面的实验:

  1. 启动发布者
  2. 发送1024个数据包,内容为递增整数
  3. 开始订阅
  4. 再发送一个 1024 作为内容

例如:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

public class SoQuestion {
    private static class Publisher {
        private DatagramSocket socket;
        private InetAddress address;
        private int port;

        private int count = 1;

        public Publisher(int port, String host) 
            throws SocketException, UnknownHostException {
            this.port = port;
            address = InetAddress.getByName(host);
            socket = new DatagramSocket();
        }

        public void publish(int packets) {
            System.out.println(
                "Publishing " + packets + 
                " packets via UDP to port " + this.port
                );

            for (int i = 1; i <= packets; i++,count++) {

                String data = "" + count;
                byte[] dataBytes = data.getBytes();

                DatagramPacket packet = new DatagramPacket(
                    dataBytes,
                    dataBytes.length,
                    address,
                    port
                    );

                try {
                    socket.send(packet);
                    System.out.println(data + " sent");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class Subscriber extends Thread {

        private int port;
        private String host;

        private InetAddress address;
        private DatagramSocket socket;

        private byte[] buffer = new byte[1024*1024];
        private volatile boolean finished;

        public Subscriber(int port, String host) throws SocketException{
            this.port = port;
            this.host = host;
        }

        @Override
        public void run() {
            try {
                address = InetAddress.getByName(host);
                socket = new DatagramSocket(port, address);
                socket.setSoTimeout(5000);

                System.out.println("Subscribed via UDP to port " + port);
                while (!finished) {

                    DatagramPacket packet = new DatagramPacket(
                        buffer,
                        buffer.length
                        );

                    socket.receive(packet);
                    System.out.println(
                        new String(
                            packet.getData(), 
                            0, 
                            packet.getLength()
                            ) + " received"
                        );

                }

                System.out.println("Subscription finished");
            } catch (SocketTimeoutException x ) {
                System.out.println("Subscription timed-out.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void finish() {
            this.finished = true;
        }
    }

    public static void main(String[] args) {
        try {
            int packets = 1024;

            Publisher publisher = new Publisher(9000, "localhost");
            publisher.publish(packets);

            Subscriber subscriber = new Subscriber(9000, "localhost");
            subscriber.start();

            Thread.sleep(2000);

            publisher.publish(packets);

            //subscriber.finish();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

当我 运行 这与:

javac SoQuestion.java
java SoQuestion|sort -k 1,1n -k 2,2r

我发现前 1024 个数据包没有接收到,并且在 x64 Linux 3.16.7-29 上看到下一个 1024 个匹配接收:

1 sent
...
1024 sent
1025 sent
1025 received
...
2048 sent
2048 received

使用DatagramSocket 发送数据不需要另一端的侦听器。数据哪儿也去不了。它也不会存储在等待侦听器套接字打开的任何地方。您看到的行为很可能是由您的代码引起的。

你真的应该对你捕获的异常做些事情,而不是忽略它们。

您还应该确保停止您的 Question18Server。 ServerSocket 在 receive() 中并且没有关闭。

你真的需要为客户端使用线程吗?难道你不能 运行 这作为你的主要部分 class 而不启动一个线程。

我认为如果您解决了这些问题,您将开始了解真正发生的事情。