在一个端口上运行多个DatagramChannel的最佳方法

时间:2018-12-13 11:30:38

标签: java udp

最近,我一直在使用DatagramChannels做很多事情,并且制作了一个非常复杂且功能良好的系统,可以在连接的两个侧面上使用它们。 但是,我遇到了一个问题。在检查连接协议的完整性时,我意识到在一个端口上运行多个通道存在很大的问题。似乎与Java的TCP套接字不同,Java的udp套接字在数据包到达适当的通道时不能正确地路由它们。 更具体地说,如果我在两个线程上有两个通道,等待一个数据包,它们都在同一端口上,但连接到不同的输出,则似乎第一个绑定的通道将是获取数据包的通道,即使它来自另一个通道的连接。问题在于它会自动(按理应)将其过滤掉,结果,该数据包将永远丢失,第二个信道将一直处于等待状态。 由于技术限制,我需要服务器端仅在一个端口上运行,这使我对应该做什么感到困惑。 难道我做错了什么?另外,这是使用选择器修复的吗?我对DatagramSockets和通道有点陌生,因为到目前为止,我在Java中大多使用过tcp。

我这里也有一些完整性检查的测试代码来验证它,这有点混乱,但是有两个计时器不断地尝试从同一连接接收数据包,只有其中一个正在接收。 通过更改首先运行哪个计时器任务,我可以更改哪个计时器获取数据包。更令我感到困惑的是,第一个计时器任务能够与现在的每个套接字一起工作,而仍然不让“外部通道”接收单个数据包。

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);
    }
}

1 个答案:

答案 0 :(得分:0)

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