Java 侦听同一端口上的 TCP 和 UDP 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2819274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:14:42  来源:igfitidea点击:

Listening for TCP and UDP requests on the same port

javasocketstcpudpport

提问by msaveski

I am writing a Client/Server set of programs

我正在编写一组客户端/服务器程序

Depending on the operation requested by the client, I use make TCP or UDP request.

根据客户端请求的操作,我使用 make TCP 或 UDP 请求。

Implementing the client side is straight-forward, since I can easily open connection with any protocol and send the request to the server-side.

实现客户端很简单,因为我可以轻松地打开与任何协议的连接并将请求发送到服务器端。

On the servers-side, on the other hand, I would like to listen both for UDP and TCP connections on the same port. Moreover, I like the the server to open new thread for each connection request.

另一方面,在服务器端,我想侦听同一端口上的 UDP 和 TCP 连接。而且,我喜欢服务器为每个连接请求打开新线程。

I have adopted the approach explained in: link text

我采用了解释的方法:链接文本

I have extended this code sample by creating new threads for each TCP/UDP request.

我通过为每个 TCP/UDP 请求创建新线程来扩展此代码示例。

This works correctly if I use TCP only, but it fails when I attempt to make UDP bindings.

如果我只使用 TCP,这可以正常工作,但是当我尝试进行 UDP 绑定时它会失败。

Please give me any suggestion how can I correct this.

请给我任何建议,我该如何纠正这个问题。

tnx

tnx

Here is the Server Code:

这是服务器代码:

public class Server {
public static void main(String args[]) {
    try {
        int port = 4444;
        if (args.length > 0)
            port = Integer.parseInt(args[0]);

        SocketAddress localport = new InetSocketAddress(port);

        // Create and bind a tcp channel to listen for connections on.
        ServerSocketChannel tcpserver = ServerSocketChannel.open();
        tcpserver.socket().bind(localport);

        // Also create and bind a DatagramChannel to listen on.
        DatagramChannel udpserver = DatagramChannel.open();
        udpserver.socket().bind(localport);

        // Specify non-blocking mode for both channels, since our
        // Selector object will be doing the blocking for us.
        tcpserver.configureBlocking(false);
        udpserver.configureBlocking(false);

        // The Selector object is what allows us to block while waiting
        // for activity on either of the two channels.
        Selector selector = Selector.open();

        tcpserver.register(selector, SelectionKey.OP_ACCEPT);
        udpserver.register(selector, SelectionKey.OP_READ);

        System.out.println("Server Sterted on port: " + port + "!");

        //Load Map
        Utils.LoadMap("mapa");
        System.out.println("Server map ... LOADED!");

        // Now loop forever, processing client connections
        while(true) {
            try { 
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();

                // Iterate through the Set of keys.
                for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext();) {
                    SelectionKey key = i.next();
                    i.remove();

                    Channel c = key.channel();

                    if (key.isAcceptable() && c == tcpserver) {
                        new TCPThread(tcpserver.accept().socket()).start();
                    } else if (key.isReadable() && c == udpserver) {
                        new UDPThread(udpserver.socket()).start();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println(e);
        System.exit(1);
    }
}

}

}

The UDPThread code:

UDPThread 代码:

public class UDPThread extends Thread {
private DatagramSocket socket = null;

public UDPThread(DatagramSocket socket) {
    super("UDPThread");
    this.socket = socket;
}

@Override
public void run() {
    byte[] buffer = new byte[2048];
    try {           
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        socket.receive(packet);

        String inputLine = new String(buffer);
        String outputLine = Utils.processCommand(inputLine.trim());

        DatagramPacket reply = new DatagramPacket(outputLine.getBytes(), outputLine.getBytes().length,
                                                  packet.getAddress(), packet.getPort());
        socket.send(reply);

    } catch (IOException e) {
        e.printStackTrace();
    }
    socket.close(); 
}

}

}

I receive:

我收到:

Exception in thread "UDPThread" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.DatagramSocketAdaptor.receive(Unknown Source)
at server.UDPThread.run(UDPThread.java:25)

10x

10倍

回答by Stephen C

AFAIK, you should be able to listen for both TCP connections and UDP messages on the same port. It would help if you posted your UDP code, and the exception + stacktrace that you are seeing.

AFAIK,您应该能够在同一端口上侦听 TCP 连接和 UDP 消息。如果您发布了 UDP 代码以及您看到的异常 + 堆栈跟踪,这会有所帮助。

回答by Chris Dennett

It should work. One of the problems with this code, it seems, is that the ByteBuffer size is set to 0, meaning that the datagram is discarded (as it mentions in the comments). If you need to receive any information over UDP and you are on a reliable network, you can set the size quite big and receive big datagrams made up of multiple packets. Otherwise, on an unreliable network, set this to the MTU size. Make sure you flip() the ByteBuffer after receiving anything in it.

它应该工作。这段代码的问题之一似乎是 ByteBuffer 大小设置为 0,这意味着数据报被丢弃(正如它在评论中提到的那样)。如果您需要通过 UDP 接收任何信息并且您在可靠的网络上,您可以将大小设置得相当大并接收由多个数据包组成的大数据报。否则,在不可靠的网络上,将其设置为 MTU 大小。确保在接收到其中的任何内容后翻转()ByteBuffer。

Also, creating new threads for each request is a bad idea, create a 'session' thread for each different IP you receive in a HashMap or something, and then do a guarded blockon the session object. Wake up the thread sleeping on that object when you receive a message after passing in new information. The selector code you have is designed to avoidthe creation of threads in this way.

此外,为每个请求创建新线程是一个坏主意,为您在 HashMap 或其他东西中收到的每个不同 IP 创建一个“会话”线程,然后在会话对象上执行受保护的块。当您在传入新信息后收到消息时,唤醒在该对象上休眠的线程。您拥有的选择器代码旨在避免以这种方式创建线程。

Edit: based on the above code, you're create a datagram channel and then using the socket to receive datagrams directly? That's doesn't make sense. Use the channel methods only after binding the channel. Also, don't do this in a separate thread. Your code isn't thread-safe and will bust itself up. Hand the received information off to the separate 'session' thread as mentioned earlier. The selector is designed to tell you what channels can be read from without blocking (although blocking is disabled anyway, so it will tell you what channels have data to be read from).

编辑:基于上面的代码,您创建了一个数据报通道,然后使用套接字直接接收数据报?那是没有意义的。只有在绑定频道后才能使用频道方法。另外,不要在单独的线程中执行此操作。您的代码不是线程安全的,会自行崩溃。如前所述,将接收到的信息传递给单独的“会话”线程。选择器旨在告诉您可以在不阻塞的情况下读取哪些通道(尽管无论如何都禁用了阻塞,因此它会告诉您哪些通道具有要读取的数据)。

回答by user207421

You can't use DatagramSocket.receive()in non-blocking mode. You have to use the read()or receive()methods of your DatagramChanneldirectly.

您不能DatagramSocket.receive()在非阻塞模式下使用。您必须直接使用您的read()receive()方法DatagramChannel

In fact as you are using non-blocking mode and a Selector, it is quite impossible to see why you're also using a UDPThreadat all. Just call udpserver.receive()instead of starting the thread.

事实上,当您使用非阻塞模式和 a 时Selector,完全不可能明白您为什么还要使用 a UDPThread。只需调用udpserver.receive()而不是启动线程。