So I'm working on this recreational project to learn more about java networking and so far every tutorial or documentation I've come across involves creating a new thread for each client connection to wait for input. I'm wondering if it's possible to handle the list of client connections with a single thread? I tried doing something like the following code but it didn't work.
while(true){
for(Client c : list){
DataInputStream dis = new DataInputStream(c.getSocket().getInputStream());
if(dis.readLine()!=null){
//Code
}
dis.close();
}
}
Yes it is possible with a single thread using the NIO package. This will allow you to set up non-blocking IO and multiplex across channels within your single thread. It's not exactly trivial but there's a decent example here.
Your example above will block on the readLine() call until data is available on the Socket. If one of your clients is waiting on data, the while loop will never proceed and you'll never service the other clients.
Related
I am working on a application which should be very light weight and use minimum number of threads.
I need a socket server in my application for heartbeat monitor.
ServerSocket listener= new ServerSocket(port);
while (true) {
Socket socket = listener.accept();
Runnable thread = new HBClient(this, socket);
thread.run();
}
Problem here is I have to use one thread per one client.
Is there a way to do this without using threads? Maybe an event driven approach to identify when a client is connected or a non blocking method to accept clients.(I already checked java.nio but it seems even that cannot be used without threads)
Using NIO (for New IO, not Non-blocking IO) you can use a Selector on a single thread to handle multiple channels whereas with basic IO you have one thread responsible for one task (accepting connections or doing communication on a connection).
The basic premise is that you have resources and the single selector will "spin around" and choose one of them to process for whatever needs to be done (connect, read, write). Once that's done, another resource will be selected and so on. Of course a resource won't be selected unless there's actually something to do, and the channels inform that with SelectionKey flags to indicate which operations can be done.
However using non-blocking IO is a lot harder to program to than basic IO, and if you're not handling a lot of resources it won't be that much of an [improvement](NIO Performance Improvement compared to traditional IO in Java
) either. Even if you do want to use NIO it's recommended that unless you do NIO for learning purposes, use an existing framework like Netty that will make it a lot easier for you to concentrate on the functionality of the program and not the intricacies of getting NIO to work properly.
If you do want to spend time with NIO, there are plenty of questions on SO that discuss it like Java NIO Server
No. Even if you try to implement an event driven approach, someone should still listen to the socket to throw an event. So it is basically impossible to do this with a single thread.
But, you can break the infinite loop when you notify a connected client. You won't be accepting new clients but you'll be in a single thread.
I want to write a chat application in java which can handle many users simultaneously. I read about sockets and threadpools to limit thread number, but I can't imagine how to handle e.g. 100 socket connections at the same time and do not create 100 new threads. Idea is that client connects at the beginning and his connection stays opened until he leaves the chat. He can send data to server as well as receive other users messages.
Read from socket is blocking operation, so I would need to check all user's sockets in loop with some timeout if new data is available in particular socket connection? My first idea was to create e.g. 3 threads for handling input from all connected users and 3 threads for outcomming communication from server to clients, but how can I achieve that? Is there any async API for sockets in Java where can I define threadpools for in/out communication?
Make a Client class that extends Thread. Write all the methods and in the void run() method, write the code you want executed when the client connection is made.
On the Server side, listen for new connections. Accept a new connection, get the information about the connection, pass it in the constructor to create a new Client object, and add it to an ArrayList to keep track of all ongoing connections and execute the start() method. So, all the Client objects are in an Arraylist, and the they keep running at the same time.
I had made such a chat application about an year ago. And do not forget to close the connection once the Client disengages, orelse all the objects pile up and slow up the application. I learnt that the hard way.
Use Netty as it provides an NIO framework (non-blocking IO) so that you do not need 1 thread per connection. It is a little bit (or a lot..) more complicated to write a server using non-blocking IO, but there are performance gains in regards to not requiring one thread per connection.
However, 100 threads is not so many, so you could still create your server using standard IO and one thread per connection, it just depends on how much you need to scale.
For a server setup using Netty, you create a channel to which new connections are assigned. This channel is an ordered series of handlers which process incoming (and outgoing) messages from a connection / client. The handlers themselves all need to be asynchronous such that when a handler needs to return a message to the client it writes it asynchronously (non-blockingly) to the channel and receives a future back to which it can attach actions for when the message is actually written.
There is a little bit of a learning curve, but it is not that steep and the overall design of your application will be much better if built the Netty way vs using standard blocking IO.
I am building a server in java that communicates with several clients at the same time, the initial approach we had is the the server listens to connections from the clients, once a connection is received and a socket is created, a new thread is spawned to handle the communication with each client, that is read the request with an ObjectInputStream, do the desired operation (fetch data from the DB, update it, etc.), and send back a response to the client (if needed). While the server itself goes back to listen to more connections.
This works fine for the time being, however this approach is not really scalable, it works great for a small amount of clients connected at the same time, however since every client spawns another thread, what will happen when there are a too many clients connected at once?
So my next idea was to maintain a list of sorts that will hold all connected clients (the socket object and some extra info), use a ThreadPool for to iterate through them and read anything they sent, if a message was received then put it in a queue for execution by another ThreadPool of worker threads, and once the worker has finished with its task if a response is required then send it.
The 2 latter steps are pretty trivial to implement, the problem is that with the original thread per client implementation, I use ObjectInputStream.readObject() to read the message, and this method blocks until there is something to read, which is fine for this approach, but I can't use the same thing for the new approach, since if I block on every socket, I will never get to the ones that are further down the list.
So I need a way to check if I have anything to read before I call readObject(), so far I tried the following solutions:
Solution 1:
use ObjectInputStream.available() to check if there is anything available to read, this approach failed since this method seems to always return 0, regardless of whether there is an object in the stream or not. So this does not help at all.
Solution 2:
Use PushbackInputStream to check for the existence of the first unread byte in the stream, if it exists then push it back and read the object using the ObjectInputStream, and if it doesn't move on:
boolean available;
int b = pushbackinput.read();
if (b==-1)
available = false;
else
{
pushbackinput.unread(b);
available = true;
}
if (available)
{
Object message= objectinput.readObject();
// continue with what you need to do with that object
}
This turned out to be useless too, since read() blocks also if there is no input to read. It seems to only return the -1 option if the stream was closed. If the stream is still open but empty it just blocks, so this is no different than simply using ObjectInputStream.readObject();
Can anyone suggest an approach that will actually work?
This is a good question, and you've done some homework.... but it involves going through some history to get things right. Note, your issue is actually more to do with the socket-level communication rather than the ObjectInputStream:
The easiest way to do things in the past was to have a separate thread per socket. This was scalable to a point but threads were expensive and slow to create.
In response, for large systems, people created thread pools and would service the sockets on threads when there was work to do. This was complicated.
The Java language was then changed with the java.nio package which introduced the Selector together with non-blocking IO. This created a reliable (although sometimes confusing) way to service multiple sockets with fewer threads. In your case through, it would not help fully/much because you want to know when a full Object is ready to be read, not when there's just 'some' object.
In the interim the 'landscape' changed, and Java is now able to more efficiently create and manage threads. 'Current' thinking is that it is better/faster and easier to allocate a single thread per socket again.... see Java thread per connection model vs NIO
In your case, I would suggest that you stick with the thread-per-socket model, and you'll be fine. Java can scale and handle more threads than sockets, so you'll be fine.
I'm the main developer of an online game.
Players use a specific client software that connects to the game server with TCP/IP (TCP, not UDP)
At the moment, the architecture of the server is a classic multithreaded server with one thread per connection.
But in peak hours, when there are often 300 or 400 connected people, the server is getting more and more laggy.
I was wondering, if by switching to a java.nio.* asynchronous I/O model with few threads managing many connections, if the performances would be better.
Finding example codes on the web that cover the basics of such a server architecture is very easy. However, after hours of googling, I didn't find the answers to some more advanced questions:
1 - The protocol is text-based, not binary-based. The clients and the server exchanges lines of text encoded in UTF-8. A single line of text represents a single command, each lines are properly terminated by \n or \r\n.
For the classic multithreaded server, I have that kind of code :
public Connection (Socket sock) {
this.in = new BufferedReader( new InputStreamReader( sock.getInputStream(), "UTF-8" ));
this.out = new BufferedWriter( new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
new Thread(this) .start();
}
And then in run, data are read line by line with readLine.
In the doc, I found an utilitiy class Channels that can create a Reader out of a SocketChannel. But it is said that the produced Reader wont work if the Channel is in non-blocking mode, what contradicts the fact that non-blocking mode is mandatory to use the highly performant channel selection API I'm willing to use. So, I suspect that it isn't the right solution for what I would like to do.
The first question is therefore the following: if I can't use that, how to efficiently and properly take care of breaking lines and converting native java strings from/to UTF-8 encoded data in the nio API, with buffers and channels?
Do I have to play with get/put or inside the wrapped byte array by hand? How to go from ByteBuffer to strings encoded in UTF-8 ? I admit to don't understand very well how to use classes in the charset package and how it works to do that.
2 - In the asynchronous/non-blocking I/O world, what about the handling of consecutive read/write that have by nature to be executed sequencially one after the other?
For example, the login procedure, which is typicly challenge-response-based: the server sends a question (a particular computation), the client sends the response, and then the server checks the response given by the client.
The answer is, I think, certainly not to make a single task to send to worker threads for the whole login process, as it is quite long, with the risk to freeze worker threads for too much time (Imagine that scenario: 10 pool threads, 10 players try to connect at the same time; tasks related to players already online are delayed until one thread is again ready).
3 - What happens if two different threads simultaneously call Channel.write(ByteBuffer) on the same Channel?
Do the client might receive mixed up lines ? For example if a thread sends "aaaaa" and another sends "bbbbb", could the client receive "aaabbbbbaa", or am I ensured that everyting is sent in a consist order? Am I allowed to modify the buffer used right after the call returned?
Or asked differently, do I need additional synchronization to avoid this sort of situation?
If I need additionnal synchronization, how to know when release locks and so on, upon write finishes?
I'm afraid that the answer isn't as simple as registering for OP_WRITE in the selector. By trying that, I noticed that I get the write-ready event all the time and always for all clients, exiting Selector.select early mostly for nothing, since there are only 3 or 4 messages to send pers second per client, while the selection loop is performed hundreds of times per second. So, potentially, active wait in perspective, what is very bad.
4 - Can multiple threads call Selector.select on the same selector simultaneously without any concurrency problems such as missing an event, scheduling it twice, etc?
5 - In fact, is nio as good as it is said to be ? Would it be interesting to stay to classic multithreaded model, but unstead of creating a thread per connection, use fewer threads and loop over the connections to look for data availability using InputStream.isAvailable ? Is that idea stupid and/or inefficient?
1) Yes. I think that you need to write your own nonblocking readLine method. Note also that a nonblocking read may be signaled when there are several lines in the buffer, or when there is an incomplete line:
Example: (first read)
USER foo
PASS
(second read)
bar
You will need to store (see 2) the data that was not consumed, until enough information is ready to process it.
//channel was select for OP_READ
read data from channel
prepend data from previous read
split complete lines
save incomplete line
execute commands
2) You will need to keep the state of each client.
Map<SocketChannel,State> clients = new HashMap<SocketChannel,State>();
when a channel is connected, put a fresh state into the map
clients.put(channel,new State());
Or store the current state as the attached object of the SelectionKey.
Then, when executing each command, update the state. You may write it as a monolithic method, or do something more fancy such as polymorphic implementations of State, where each state knows how to deal with some commands (e.g. LoginState expects USER and PASS, then you change the state into a new AuthorizedState).
3) I don't recall using NIO with many asynchronous writers per channel, but the documentation says it is thread safe (I won't elaborate, since I have no proof of this). About OP_WRITE, note that it signals when the write buffer is not full. In other words, as said here: OP_WRITE is almost always ready, i.e. except when the socket send buffer is full, so you will just cause your Selector.select() method to spin mindlessly.
4) Yes. Selector.select() performs a blocking selection operation.
5) I think that the most difficult part is switching from a thread-per-client architecture, to a different design where reads and writes are decoupled from processing. Once you have done that, it is easier to work with channels than working your own way with blocking streams.
Is there any possibility to communicate with clients by events? I mean:
I have connected client, InputStreamReader and PrintWriter
in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
when I use in.readLine() server waits for incoming data. But i have this situation:
Client didn't send any data
Connection is still alive
I need to send some data to client (but in.readLine() is still hanging process) and wait for respond
The questions are:
What is the best way to handle asynchronously incoming data? I mean something like "events". Should I create thread for read and another thread for write? If i can do it in one thread, could you give an example of the code please?
Is possible to abort waiting for in.readLine()?
Java provides non-blocking i/o through the java.nio package (see here). But Java's "nio" channels do not inter-operate with streams from java.io. So, if you want to use nio, you'll have to build your server with nio from the listener on down.
If you're stuck with the existing java.io streams, then you'll either have to use a thread-per-client model; or you'll need to devise a system for having a single thread (or pool of threads) manage a bunch of clients by looping over them repeatedly, polling instream.available() to figure out which ones have data ready to be handled. Of course, in this latter case, you'd want to avoid busy-looping, so some appropriate use of Thread.sleep is probably also in-order.
In my opinion having a separate thread to perform socket IO is best if you want your program to behave asynchronously. Have a look at http://en.wikipedia.org/wiki/Observer_pattern.
For a simple application, what I'll do is create a separate thread to listen for incoming data, and register 'observers' or 'event listener' to this thread. When a data comes in, notify your observers so they can perform necessary actions.
While the listener thread is idle waiting for data, your main thread still can progress normally.
Make sure you're also familiar with Java concurrency programming