Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid blocking with Java ServerSocket?

Im working on a socket listener that has to listen on 2 ports for 2 types of data( port 80 and port 81). These data are very similar as in the kind of operations that are performed on the data and are just different because they arrive n different ports. I went ahead and coded an implementation using Java's ServerSocket class, only to realize later that the accept() method of the ServerSocket class is block and my implementation cant afford that. So now i was thinking of implementing the same using Java NIO but after having gone through some tutorials i think i am more confused than how i started. It would be great if someone here could walk me through the whole process, even of it be in pseudo code or jus technical "what to do next"'s. This is what i plan to achieve.

Listen, like for ever on 2 ports by calling 2 similar threads.(non blocking) A remote device from some network location connects, sends data and then disconnects.

I think if only knowledge of how NIO can be used to set up a server to listen on a port say port 80, on localhost,is achieved, the rest is all pretty easy to implement.

Cheers

like image 471
ping Avatar asked Jan 23 '10 05:01

ping


People also ask

Is ServerSocket accept blocking?

accept() is a blocking one. There must be a smart way to get around this.

How do I make a non-blocking socket?

To mark a socket as non-blocking, we use the fcntl system call. Here's an example: int flags = guard(fcntl(socket_fd, F_GETFL), "could not get file flags"); guard(fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK), "could not set file flags"); Here's a complete example.

Is socket accept blocking Java?

Socket programs in Java can be made to work in both blocking and non-blocking mode. In blocking socket mode, a system call event halts the execution until an appropriate reply has been received.


2 Answers

Here a little example to get started with NIO.

It's a server listening on ports 80 and 81 and printing everything that is received on standard output. A connection is closed after receiving a packet starting with CLOSE; the whole server is shutdown after receiving a packet starting with QUIT. Missing the sending part and error handling could be a bit better. :-)

public static void main() throws IOException {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    Selector selector = Selector.open();

    ServerSocketChannel server1 = ServerSocketChannel.open();
    server1.configureBlocking(false);
    server1.socket().bind(new InetSocketAddress(80));
    server1.register(selector, OP_ACCEPT);

    ServerSocketChannel server2 = ServerSocketChannel.open();
    server2.configureBlocking(false);
    server2.socket().bind(new InetSocketAddress(81));
    server2.register(selector, OP_ACCEPT);

    while (true) {
        selector.select();
        Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
        while (iter.hasNext()) {
            SocketChannel client;
            SelectionKey key = iter.next();
            iter.remove();

            switch (key.readyOps()) {
                case OP_ACCEPT:
                    client = ((ServerSocketChannel) key.channel()).accept();
                    client.configureBlocking(false);
                    client.register(selector, OP_READ);
                    break;
                case OP_READ:
                    client = (SocketChannel) key.channel();
                    buffer.clear();
                    if (client.read(buffer) != -1) {
                        buffer.flip();
                        String line = new String(buffer.array(), buffer.position(), buffer.remaining());
                        System.out.println(line);
                        if (line.startsWith("CLOSE")) {
                            client.close();
                        } else if (line.startsWith("QUIT")) {
                            for (SelectionKey k : selector.keys()) {
                                k.cancel();
                                k.channel().close();
                            }
                            selector.close();
                            return;
                        }
                    } else {
                        key.cancel();
                    }
                    break;
                default:
                    System.out.println("unhandled " + key.readyOps());
                    break;
            }
        }
    }
}

Obs: the fields of SelectionKey (OP_ACCEPT...) are statically imported:

import static java.nio.channels.SelectionKey.*;
like image 166
user85421 Avatar answered Sep 21 '22 16:09

user85421


Many frameworks, such as Apache MINA and Netty, have been implemented based on Java NIO to boost non-blocking IO programming. I strongly recommend them to make your NIO programming a joy, rather than a nightmare. They fit your problem.

In addition, try to use an efficient protocol both in transport message size and encode/decode (serialize/deserialize) performance. Google Protocol Buffers is a reliable solution in this area. Also take a look at Kryo and KryoNet. They can be helpful.

like image 38
Amir Moghimi Avatar answered Sep 18 '22 16:09

Amir Moghimi