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

时间:2010-05-12 13:37:26

标签: java sockets tcp udp port

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

根据客户端请求的操作,我使用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倍

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:-1)

您无法在非阻止模式下使用DatagramSocket.receive()。您必须直接使用read()的{​​{1}}或receive()方法。

事实上,当您使用非阻止模式和DatagramChannel时,很难理解为什么您还使用Selector。只需拨打UDPThread而不是启动该主题。