让多个 DatagramChannels 在一个端口上工作的最佳方法

Best approach to having multiple DatagramChannels working on one port

最近我一直在与 DatagramChannels 打交道,并制作了一个相当复杂且功能良好的系统,以便在连接的两侧使用它们。 但是,我 运行 遇到了问题。在对我的连接协议进行健全性检查时,我意识到 运行 在一个端口上设置多个通道是一个很大的问题。似乎与 java 的 TCP 套接字不同,java 的 udp 套接字在数据包到达适当的通道时没有正确路由数据包。 更具体地说,如果我有两个通道,在两个线程上,等待一个数据包,都在同一个端口上,但连接到不同的输出,第一个被绑定的将是获取数据包的那个,即使它来自其他频道的连接。问题是它会自动将其过滤掉(应该如此),结果,该数据包将永远完全丢失,而第二个通道将继续等待。 由于技术限制,我需要服务器端在一个端口上 运行,这让我很困惑我应该怎么做。 难道我做错了什么?另外,这是通过使用选择器来修复的吗?我对 DatagramSockets 和通道有点陌生,因为到目前为止我主要使用 java 中的 tcp。

我这里也有一些健全性检查的测试代码来验证它,有点乱,但是有两个定时器不断地尝试从同一个连接接收数据包,而且只有一个正在接收。 通过首先更改哪个计时器任务 运行s,我可以更改哪个计时器任务获取数据包。更让我困惑的是,第一个计时器任务能够与每个现在的套接字一起工作,同时仍然不让 "outsider channel" 接收到一个数据包。

public static void main(String[] args) {
    runServer();
    runClient();
}

public static void runClient() {
    DatagramChannel channel;
    try {
        channel = DatagramChannel.open();
        channel.bind(new InetSocketAddress("localhost", 8000));
        channel.connect(new InetSocketAddress("localhost", 8001));
        while(true) {
        channel.write(ByteBuffer.wrap("let's see who wins".getBytes()));
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static class Channel{

    DatagramChannel channel;
    int number;
    public Channel(int number) {
        try {
            channel = DatagramChannel.open();
            this.number = number;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public static boolean outsiderLost = false;

// client on port 8000
// server on port 8001
public static void runServer() {
        ByteBuffer buf = ByteBuffer.wrap(new byte[300]);
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {

            @Override
            public void run() {
                boolean firstTime = true;
                int i = 0;
                Channel channel = new Channel(i);
                DatagramChannel channel1 = channel.channel;
                while(true) {
                try {
                    if(firstTime) {
                        channel1.socket().setReuseAddress(true);
                        channel1.bind(new InetSocketAddress("localhost", 8001));
                        channel1.connect(new InetSocketAddress("localhost", 8000));
                        channel1.configureBlocking(true);
                        firstTime = false;
                    } else {
                    SocketAddress add = channel1.receive(buf);
                    i++;
                    channel = new Channel(i);
                    channel1 = channel.channel;
                    channel1.socket().setReuseAddress(true);
                    channel1.bind(new InetSocketAddress("localhost", 8001));
                    channel1.connect(add);
                    buf.clear();
                    System.out.println("channel " + channel.number + " wins");
                    outsiderLost = true;
                }} catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                }
            }

        }, 500);
        Timer timer2 = new Timer();
        timer2.schedule(new TimerTask() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                DatagramChannel channel1 = null;
                try {
                    channel1 = DatagramChannel.open();
                    channel1.socket().setReuseAddress(true);
                    channel1.bind(new InetSocketAddress("localhost", 8001));
                    channel1.connect(new InetSocketAddress("localhost", 8000));
                    channel1.configureBlocking(true);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                while(!outsiderLost) {
                    try {
                        channel1.read(buf);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    buf.clear();
                    System.out.println("outsider wins");
                    outsiderLost = true;
                }
            }

        }, 1000);
    }
}

最终创建了一个 reader 实际在服务器上读取的通道,然后将数据包重新路由到不同的发送通道进行处理,然后发送响应。