Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.nio vs new thread for each socket [closed]

I am developping a 1 to many server-client application, which is a small project.

Since socket IO is blocking. I am looking for a solution for this.

Could anyone tell me what is the good/bad for each of the 2 solutions?

  1. use java.nio
  2. new a single thread for every client that connected.

Thanks

like image 474
Leon Avatar asked Feb 02 '11 16:02

Leon


People also ask

What is Nio thread in Java?

nio is a non-blocking API for socket connections, which means you are not tight to the number of threads available. With this library, one thread can handle multiple connections at once.

How is NIO non-blocking?

Java NIO is an asynchronous IO or non-blocking IO. For instance, a thread needs some data from the buffer. While the channel reads data into the buffer, the thread can do something else. Once data is read into the buffer, the thread can then continue processing it.

What is Java nio2?

Java NIO. 2 package. This package follows an asynchronous approach to non-blocking IO not supported in java. nio package. The most significant changes are related to high-level file manipulation. They are added with Files, Path, and Paths classes.


2 Answers

There's nothing wrong with either approach. If you have limited clients the second option will suffice (and possibly even thrive based on a multicore architecture), otherwise it might be beneficial to let java.nio manage your resources.

See this question on the same topic, as well as this other post, or why not consider this blog post which argues against using java.nio for most scenarios.

like image 92
Johan Sjöberg Avatar answered Sep 23 '22 00:09

Johan Sjöberg


Individual threads

  • You can use the simple InputStream/OutputStream (or Reader/Writer) API, and wrapping Streams around each other. In my current project, I'm using a stack of

    • MessageFormatter, a custom formatting class
    • PrintWriter
    • OutputStreamWriter
    • DebugOutputStream (custom class, which makes a copy for debugging purposes)
    • DeflatorOutputStream (a custom subclass, in fact, which supports flushing)
    • the OutputStream of a SSLSocket

    and the other way around on the receiving side. This is quite comfortable, since you only have to deal with the top layer in your program logic (the rest is mostly one constructor call each).

  • you need a new Thread (or even pair of threads, depending on architecture) for each connection.

    (In my project I have a MessageParser-Thread for each connection, which then gives individual jobs to a ThreadPool, and these jobs then may write to one or several of the open connections, not only the one which spawned them. The writing is synchronized, of course.)

  • each thread needs quite a bit of stack space, which can be problematic if you are on a resource limited machine.

In the case of short-lived connections, you actually don't want a new thread for each connection, but only a new Runnable executed on a ThreadPool, since construction of new Threads takes a bit of time.

nonblocking IO

  • if you have such a multi-layer architecture with multiple conversions, you have to arrange it all by yourself. It is possible:

    • my MessageFormatter can write to a CharBuffer as well as to a Writer.
    • for the Char-to-Byte-formatting, use CharsetEncoder/CharsetDecoder (transfers data between CharBuffer and ByteBuffer).
    • for the compressing, I used wrapper classes around Deflater/Inflater, which transfers data between two ByteBuffers.
    • for the encryption, I used a SSLEngine (one for each connection), which also uses ByteBuffers for input and output.
    • Then, write to a SocketChannel (or in the other direction, read from it).

    Still, the management overhead is quite a hassle, as you must track where the data is. (I actually had a two pipelines for each connection, and one or two threads managing the data in all the pipelines, between this waiting on a Selector for new data on the sockets (or for new space there, in the outgoing case). (The actual processing after the parsing of the Messages still took place in spawned Runnables in a ThreadPool.)

  • You need only a small number of threads. This was actually the reason I tried to do this asynchronously. It worked (with the same synchronous client), but was way slower than my multi-thread solution, so I put this back until we run out of memory for too much threads. (Until now, there are not so much connections at the same time.)

like image 24
Paŭlo Ebermann Avatar answered Sep 23 '22 00:09

Paŭlo Ebermann