views:

315

answers:

3

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

A: 

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.

Stephen C
+1  A: 

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

Chris Dennett
I can see your point with the use of session thread, I agree with you but for my purpose it is better to make one thread per connection.for the ByteBuffer, I not using it looks like u didn't see the code that I have uploaded, just the link.tnx
UDP sockets don't have connections. Only TCP sockets do. The thread just keeps the processing away from the main selection thread. Anyway, this is how Apache MINA works :)
Chris Dennett
Aham, if I understood you right you say there is no point in making new UDP thread, but rather to recieve the information in the server class and that, further process it in a new thread?BTW, is there any more elegant way to do the same thing, listen for both tcp and udp requests?
Threads aren't needed, but they increase scalability (it allows the selection thread to do more work, if you find you can't process that much data). As for listening for both UDP and TCP requests, have two selector threads, one for TCP and one for UDP, and you can separate the logic.
Chris Dennett
hmmm, I understand your point, but I am not sure how to achieve that
Read the datagram, pass it into the thread via a setNextDatagramMessage(..) method and store it as a field, and then wake up the thread :) It will store the datagram, set the field to null, and then consume that datagram. You can do the same with TCP. Whatever object you get as a result (ByteBuffer or otherwise), just pass that through.
Chris Dennett
+1  A: 

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

EJP