Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listening for TCP and UDP requests on the same port

I am writing a Client/Server set of programs

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

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 server to open new thread for each connection request.

I have adopted the approach explained in: link text

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

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

Please give me any suggestion how can I correct this.

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:

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

like image 784
msaveski Avatar asked May 12 '10 13:05

msaveski


People also ask

Can you listen to TCP and UDP on the same port?

Yes, you can use the same port number for both TCP and UDP. Many protocols already do this, for example DNS works on udp/53 and tcp/53.

Can two processes listen on same port?

For TCP, no. You can only have one application listening on the same port at one time. Now if you had 2 network cards, you could have one application listen on the first IP and the second one on the second IP using the same port number.

Can use both TCP and UDP at the same time?

TCP and UDP portsBoth TCP and UDP protocols use ports. You can have an application running on a computer using TCP port 80 and another application using UDP port 80. An application address is effectively: IP address + protocol (TCP or UDP) + port number.

Are TCP and UDP ports separate?

No, TCP and UDP don't share ports. Each port is exclusively either TCP or UDP.


1 Answers

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.

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 block on 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 avoid the creation of threads in this way.

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

like image 84
Chris Dennett Avatar answered Oct 08 '22 12:10

Chris Dennett