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

Posted

技术标签:

【中文标题】在同一端口上侦听 TCP 和 UDP 请求【英文标题】:Listening for TCP and UDP requests on the same port 【发布时间】:2011-02-18 15:25:05 【问题描述】:

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

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

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

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

我采用了在:link text中解释的方法

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

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

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

tnx

这是服务器代码:

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

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

我收到:

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

10 倍

【问题讨论】:

失败怎么办?您是否有更多信息,例如错误消息或示例代码? 请详细说明“失败”。 【参考方案1】:

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

事实上,当您使用非阻塞模式和Selector 时,根本无法理解为什么您还使用UDPThread。只需调用udpserver.receive() 而不是启动线程。

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

我可以通过使用会话线程来理解您的观点,我同意您的观点,但出于我的目的,最好每个连接创建一个线程。对于 ByteBuffer,我没有使用它看起来你没有看到我上传的代码,只是链接。 tnx UDP 套接字没有连接。只有 TCP 套接字可以。该线程只是使处理远离主选择线程。无论如何,这就是 Apache MINA 的工作原理 :) Aham,如果我没听错的话,您说创建新的 UDP 线程没有意义,而是在服务器类中接收信息,然后在新线程中进一步处理它?顺便说一句,有没有更优雅的方法来做同样的事情,同时监听 tcp 和 udp 请求? 线程不是必需的,但它们增加了可扩展性(它允许选择线程做更多的工作,如果你发现你不能处理那么多数据)。至于同时监听UDP和TCP请求,有两个selector线程,一个用于TCP,一个用于UDP,可以分开逻辑。 嗯,我明白你的意思,但我不知道如何做到这一点【参考方案3】:

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

【讨论】:

以上是关于在同一端口上侦听 TCP 和 UDP 请求的主要内容,如果未能解决你的问题,请参考以下文章

TCP和UDP可以同时监听相同的端口吗

C# TCP 在同一个本地端口上侦听和连接

TCP和UDP使用同一端口通信

请问同一台机器上udp端口和tcp端口可以一样对吗,不会冲突?

多个套接字可以与 UDP 的同一端口相关联吗?

如何找出哪个进程正在侦听 Windows 上的 TCP 或 UDP 端口?