I am writing a socket server using java.nio. As I need my server to use 0 threads I am using java.nio.channels.Selector. My code look as follows.
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) { // Accept client connections
this.acceptClient(key);
} else if (key.isReadable()) { // Read from client
this.read(key);
} else if (key.isWritable()) {
this.write(key);
}
}
private void acceptClient(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
SocketAddress clientAddress= channel.getRemoteAddress();
//clients is a Hashmap
clients.put(clientAddress, new Client());
clientConnected(clientAddress.toString());
System.out.println("Connected to: " + clientAddress);
channel.register(this.selector, SelectionKey.OP_READ);
}
As you can see I am creating a new Client object per accepted client. What I need to do is, relevant Client object to handle their own reading and writing.
My approach is to uniquely identify clients with their address and forward it to relevant Client object.
I think using client address to uniquely identify clients is not a good approach. What is the best way to handle this issue?
I think using client address to uniquely identify clients is not a good approach.
Nothing wrong with it. The semantics of TCP/IP guarantee that every accepted socket has a different remote SocketAddress.
But you don't need it, or the Map either. Just save the Client as the attachment of the SelectionKey. That way also the Client will disappear along with the SelectionKey automatically when you close the Channel.
By contrast, changing to an IdentityHashMap<SelectionKey, Client> as suggested elsewhere gives you an opportunity to leak the SelectionKey and therefore its Channel and the Client too.
When you register the Channel with the Selector:
channel.register(this.selector, SelectionKey.OP_READ);
It will return a SelectionKey which you can use later when you select from the Selector.
Use that key to populate an IdentityHashMap<SelectionKey,Client> so you can direct the IO to the correct Client instance. As EJP points out in his answer, this will leak SelectionKeys if not properly cleaned up. If you preferred not to clean those up, you could use a WeakHashMap as well but then you're relying in implicit behavior instead of the explicit behavior of the Selector.
EJP's suggestion to use an attachment is probably the best bet. Although I can imagine a more complicated scenario where you might want to maintain additional attachments and maybe refactoring the attachment to be an encapsulation is of high cost.
Related
I have a Java NIO server which receives data from clients.
When a channel is ready for read i.e key.isReadable() return true read(key) is called to read data.
Currently I am using a single read buffer for all channels and in read() method , I clear the buffer and read into it and then finally put into a byte array , supposing that I will get all data in one shot.
But let's say I do not get complete data in one shot(I have special characters at data ending to detect).
Problem :
So now how to keep this partial data with channel or how to deal with partial read problem ? or globally ?
I read somewhere attachments are not good.
Take a look at the Reactor pattern. Here is a link to basic implementation by professor Doug Lea:
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
The idea is to have single reactor thread which blocks on Selector call. Once there are IO events ready, reactor thread dispatches the events to appropriate handlers.
In pdf above, there is inner class Acceptor within Reactor which accepts new connections.
Author uses single handler for read and write events and maintains state of this handler. I prefer to have separate handlers for reads and writes but this is not as easy to work with as with 'state machine'. There can be only one Attachment per event, so some kind of injection is needed to switch read/write handlers.
To maintain state between subsequent read/writes you will have to do couple of things:
Introduce custom protocol which tells you when the message is fully read
Have timeout or cleanup mechanism for stale connections
Maintain client specific sessions
So, you can do something like this:
public class Reactor implements Runnable{
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
public Reactor(int port) throws IOException {
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);
// let Reactor handle new connection events
registerAcceptor();
}
/**
* Registers Acceptor as handler for new client connections.
*
* #throws ClosedChannelException
*/
private void registerAcceptor() throws ClosedChannelException {
SelectionKey selectionKey0 = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
selectionKey0.attach(new Acceptor());
}
#Override
public void run(){
while(!Thread.interrupted()){
startReactorLoop();
}
}
private void startReactorLoop() {
try {
// wait for new events for each registered or new clients
selector.select();
// get selection keys for pending events
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> selectedKeysIterator = selectedKeys.iterator();
while (selectedKeysIterator.hasNext()) {
// dispatch even to handler for the given key
dispatch(selectedKeysIterator.next());
// remove dispatched key from the collection
selectedKeysIterator.remove();
}
} catch (IOException e) {
// TODO add handling of this exception
e.printStackTrace();
}
}
private void dispatch(SelectionKey interestedEvent) {
if (interestedEvent.attachment() != null) {
EventHandler handler = (EventHandler) interestedEvent.attachment();
handler.processEvent();
}
}
private class Acceptor implements EventHandler {
#Override
public void processEvent() {
try {
SocketChannel clientConnection = serverSocketChannel.accept();
if (clientConnection != null) {
registerChannel(clientConnection);
}
} catch (IOException e) {e.printStackTrace();}
}
/**
* Save Channel - key association - in Map perhaps.
* This is required for subsequent/partial reads/writes
*/
private void registerChannel(SocketChannel clientChannel) {
// notify injection mechanism of new connection (so it can activate Read Handler)
}
Once read event is handled, notify injection mechanism that write handler can be injected.
New instances of read and write handlers are created by the injection mechanism once, when new Connection is available. This injection mechanism switches handlers as needed. Lookup of handlers for each Channel is done from the Map that is filled at the connection Acceptance by the method `registerChannel().
Read and write handlers have ByteBuffer instances, and since each Socket Channel has its own pair of handlers, you can now maintain state between partial reads and writes.
Two tips to improve performance:
Try to do first read immediately when connection is accepted. Only if you don't read enough data as defined by header in your custom protocol, register Channel interest for read events.
Try to do write first without registering interest for write events and only if you don't write all the data, register interest for
write.
This will reduce number of Selector wakeups.
Something like this:
SocketChannel socketChannel;
byte[] outData;
final static int MAX_OUTPUT = 1024;
ByteBuffer output = ByteBuffer.allocate(MAX_OUTPUT);
// if message was not written fully
if (socketChannel.write(output) < messageSize()) {
// register interest for write event
SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_WRITE);
selectionKey.attach(writeHandler);
selector.wakeup();
}
Finally, there should be timed Task which checks if Connections are still alive/SelectionKeys are canceled. If client breaks TCP connection, server will usually not know of this. As a result, there will be number of Event handlers in memory, bind as Attachments to stale connections which will result with memory leak.
This is the reason why you may say Attachments are not good, but the issue can be dealt with.
To deal with this here are two simple ways:
TCP keep alive could be enabled
periodic task could check timestamp of last activity on the given Channel. If it is idle for to long, server should terminate connection.
There's an ancient and very inaccurate NIO blog from someone at Amazon where it is wrongly asserted that key attachments are memory leaks. Complete and utter BS. Not even logical. This is also the one where he asserts you need all kinds of supplementary queues. Never had to do that yet, in about 13 years of NIO.
What you need is a ByteBuffer per channel, or possibly two, one for read and one for write. You can store a single one as the attachment itself: if you want two, or have other data to store, you need to define yourself a Session class that contains both buffers and whatever else you want to associate with the channel, for example client credentials, and use the Session object as the attachment.
You really can't get very far in NIO with a single buffer for all channels.
I wrote a multithreaded gameserver application which handles multiple simultaneous connections using NIO. Unfortunately this server generates full CPU load on one core as soon as the first user connects, even when that user is not actually sending or receiving any data.
Below is the code of my network handling thread (abbreviated to the essential parts for readability). The class ClientHandler is my own class which does the network abstraction for the game mechanics. All other classes in the example below are from java.nio.
As you can see it uses a while(true) loop. My theory about it is that when a key is writable, selector.select() will return immediately and clientHandler.writeToChannel() is called. But when the handler returns without writing anything, the key will stay writable. Then select is called again immediately and returns immediately. So I got a busy spin.
Is there a way to design the network handling loop in a way that it sleeps as long as there is no data to send by the clientHandlers? Note that low latency is critical for my use-case, so I can not just let it sleep an arbitrary number of ms when no handlers have data.
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
// wait for connections
while(true)
{
// Wait for next set of client connections
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> i = keys.iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
i.remove();
if (key.isAcceptable()) {
SocketChannel clientChannel = server.accept();
clientChannel.configureBlocking(false);
clientChannel.socket().setTcpNoDelay(true);
clientChannel.socket().setTrafficClass(IPTOS_LOWDELAY);
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ClientHandler clientHanlder = new ClientHandler(clientChannel);
clientKey.attach(clientHandler);
}
if (key.isReadable()) {
// get connection handler for this key and tell it to process data
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.readFromChannel();
}
if (key.isWritable()) {
// get connection handler and tell it to send any data it has cached
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.writeToChannel();
}
if (!key.isValid()) {
ClientHandler clientHandler = (ClientHandler) key.attachment();
clientHandler.disconnect();
}
}
}
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
The problem is here. SocketChannels are almost always writable, unless the socket send buffer is full. Ergo they should normally not be registered for OP_WRITE: otherwise your selector loop will spin. They should only be so registered if:
there is something to write, and
a prior write() has returned zero.
I don't see any reason why the reading and writing must happen with the same selector. I would use one selector in a thread for read/accept operations and it will always be blocking until new data arrives.
Then, use a separate thread and selector for writing. You mention you are using a cache to store messages before they are sent on the writable channels. In practice the only time a channel would not be writable is if the kernel's buffer is full, so it will rarely not be writable. A good way to implement this would be to have a dedicated writer thread that is given messages, and sleeping; it can be either interrupt()ed when new messages should be sent, or using a take() on a blocking queue. Whenever a new message arrives, it will unblock, do a select() on all writable keys and send any pending messages; only in rare cases will a message have to remain in the cache since a channel is not writable.
I'm about to program a server but am wondering if what I have in mind is possible. My program will be outputting to multiple clients on multiple ports - each port can be accessed by multiple clients.
Normally I would use a threaded socket server, but in this case I need it working for multiple ports. The usage I have in mind is in a vague pseudocode below:
Start server
Listen for incoming connections on several ports
Identify the port being connected to
If port 1, start a thread listening to client and outputting message type x
If port 2, start a thread listening to client and outputting message type y
Hopefully that makes some sense and you can see what I'm trying to do. Simply put: listen to selected ports, create a threaded socket connection based on which port is being connected to.
Is this doable at all, or am I going to end up multi-threading threaded socket servers?
Edit: Changed wording to better reflect the question.
It's not possible to for a single instance of ServerSocket to listen to multiple ports. You can of course have multiple ServerSockets. However, as you already know, ServerSocket.accept blocks.
What you can use instead is a ServerSocketChannel. They're used in a similar way, but do not block.
If there are no pending connections when ServerSocketChannel.accept is called then it simply returns null.
You can use with a Selector which takes a set of channels and blocks until at least one has a pending connection.
I don't remember the specifics on how to use them, but this seems to be a decent code example.
edit: Here is my own example (pseudo-ish)
Selector selector = Selector.open();
int[] ports = {4000,4001,6000};
for (int port : ports) {
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
// we are only interested when accept evens occur on this socket
server.register(selector, SelectionKey.OP_ACCEPT);
}
while (selector.isOpen()) {
selector.select();
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
Socket socket = client.socket();
// create new thread to deal with connection (closing both socket and client when done)
}
}
}
// tidy up selector and channels
Hello, so let me get this straight. What you want to do is to create a server that can listen on multiple ports and when you get a new connection, you want to be able to tell which port that connection used, is this correct? Well if that's the case, you can do this very easily with use of the java.nio package.
We're going to use a Selector for readiness selection and a ServerSocketChannel to listen for incoming connectings.
First we need to declare our Selector.
Selector selector = Selector.open();
Now lets create a list of ports to listen on and start listening on them.
int ports[] = new int[] { 1234, 4321 };
// loop through each port in our list and bind it to a ServerSocketChannel
for (int port : ports) {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
Now for the SelectionKey handling process.
while (true) {
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey selectedKey = selectedKeys.next();
if (selectedKey.isAcceptable()) {
SocketChannel socketChannel = ((ServerSocketChannel) selectedKey.channel()).accept();
socketChannel.configureBlocking(false);
switch (socketChannel.socket().getPort()) {
case 1234:
// handle connection for the first port (1234)
break;
case 4321:
// handle connection for the secon port (4321)
break;
}
} else if (selectedKey.isReadable()) {
// yada yada yada
}
}
}
Perhaps a switch statement is not necessary for such a simple task, but it's for ease of reading and understanding.
Remember, this server is set up in a non-blocking asynchronous way so that all the I/O calls you perform will not block the current thread. So DO NOT initiate any new threads in the SelectionKey handling process.
Also, I know that this doesn't completely answer your question (it might, it might not) but it will in fact give you an understanding on how to use the java.nio package to create a non-blocking asynchronous server that can listen on multiple ports.
You can't listen to all ports, but you can listen to a set of them. Create one ServerSocket ( http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket%28int%29 ) for each port you want to listen to, and accept connections on each.
This should be possible with NIO, however I don't see a good reason to avoid having one thread per listener unless you have more than 1K port.
Do you really need multiple listening ports? In most cases it should be possible for one port support all kinds of clients and have the client tell the server (or the server determine what type of connection is needed)
I don't think you can listen to all ports, no. That would be quite expensive for the OS to implement, so that's simply not how port listening works.
What if multiple applications were simultaneously listening to "all" ports, to which application should the network subsystem deliver incoming packets?
I have a minimal JMS provider, which sends topic messages over UDP and queue messages over TCP.
I use a single selector to handle UDP and TCP selection keys (registering both SocketChannels and DatagramChannels).
My problem is: if I only send and receive UDP packets, everything goes well, but as soon as I start writing on a TCP socket (using Selector.wakeup() to have the selector do the actual writing), the selector enters an infinite loop, returning an empty selection key set, and eating 100% CPU.
The code of the main loop (somewhat simplified) is:
public void run() {
while (!isInterrupted()) {
try {
selector.select();
} catch (final IOException ex) {
break;
}
final Iterator<SelectionKey> selKeys = selector.selectedKeys().iterator();
while (selKeys.hasNext()) {
final SelectionKey key = selKeys.next();
selKeys.remove();
if (key.isValid()) {
if (key.isReadable()) {
this.read(key);
}
if (key.isConnectable()) {
this.connect(key);
}
if (key.isAcceptable()) {
this.accept(key);
}
if (key.isWritable()) {
this.write(key);
key.cancel();
}
}
}
synchronized(waitingToWrite) {
for (final SelectableChannel channel: waitingToWrite) {
try {
channel.register(selector, SelectionKey.OP_WRITE);
} catch (ClosedChannelException ex) {
// TODO: reopen
}
}
waitingToWrite.clear();
}
}
}
And for a UDP send (TCP send is similar):
public void udpSend(final String xmlString) throws IOException {
synchronized(outbox) {
outbox.add(xmlString);
}
synchronized(waitingToWrite) {
waitingToWrite.add(dataOutChannel);
}
selector.wakeup();
}
So, what's wrong here? Should I use 2 different selectors to handle UDP and TCP packets?
I suggest you check the return value of select() method.
try {
if(selector.select() == 0) continue;
} catch (final IOException ex) {
break;
}
Did you try debugging to see where the loop is?
Edit:
I recomend that instead of calling "remove()" on the iterator, you call selectedKeys.clear() after you iterate over them. It is possible that the implementation of the iterator, does not remove it from the underlying set.
Check that you are not registering OP_CONNECT on a connected channel.
Problem went away after upgrading to Java 1.6.0_22.
You are probably getting an IOException and ignoring it in the empty catch block. Never do that. And just continuing after an IOException is practically never the correct action. The only exception to that rule I can think of offhand is a SocketTimeoutException, and you're in non-blocking mode so you won't be getting those, and you don't get them on selectors anyway. I would want to see the content of your methods that handle connect, accept, read, and write.
Modify your design to have one thread per incoming network connection.
The selector should be used when you're using one thread to process incoming messages on multiple TCP sockets. You register each socket with the selector and then select(), which blocks until there is data available on one of them. Then you loop through each key and process the waiting data. This is the method I've always used when writing C code, and it will work, but I don't think it's the best way to do it in Java.
Java has great native thread support, which C does not. I think it makes a lot more sense to have one thread per TCP socket and not to use selectors at all. If you just do a read operation on a socket, the thread will block until data arrives or the socket is closed. This is effectively the same thing as selecting with only one registered channel.
If you want to get this to work with only one thread, you should use the selector only for TCP sockets where you want incoming connections. This way, the only time the call to select() will return is when there is incoming data waiting on one of the sockets. That thread will be asleep at all other times, and no other operation will wake it up.
I am trying to learn Java. I would like to implement a simple networked connect 4 game as well as a chat feature.
I want my network logic to be non blocking so after much study I found that SocketChannel is what I am after regrading my needs.
What has not made sense still is the lack of CallBack functions in SocketChannels.. Like one finds in C#.
My query for this time is: How do I deliver the data received to the Chat or Game form (JFrame)?
Some guidance is most welcome.
You need to use a Selector. First create a Selector to receive the events:
Selector selector = Selector.open()
Then you need to register the ServerSocketChannel with the selector:
SelectionKey acceptKey = server.register(selector, SelectionKey.OP_ACCEPT);
Then you need to use the Selector to process events as they come in (you can think of this as the "callback" part of the process:
while(true){
//how many channel keys are available
int available = selector.select();
//select is blocking, but should only return if available is >0, this is more of a sanity check
if(available == 0) continue;
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()){
SelectionKey key = keys.next();
keys.remove();
//someone is trying to connect to the server socket
if(key.isAcceptable()) doAccept(key);
//someone is sending us data
else if(key.isReadable()) doRead(key);
//we are trying to (and can) send data
else if(key.isWritable()) doWrite(key);
}
The meat will be in doAccept(), doRead(), and doWrite(). For an accept key the selection key will contain the information to create the new Socket.
doAccept(SelectionKey key){
//create the new socket
SocketChannel socket = ((ServerSocketChannel)key.channel()).accept();
//make it non-blocking as well
socket.configureBlocking(false);
...
//here you would likely have some code to init your game objects / communication protocol, etc. and generate an identifier object (used below).
//and be able to find the socket created above
...
//Since it is non blocking it needs a selector as well, and we register for both read and write events
SelectionKey socketKey = socket.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
// so we can identify the events as they come in
socketKey.attach(someSocketIndentifier);
}
The last line adds some object to the key so that the events received from the selector can be attributed to a connection (for example it might be a player in your game). So now you can accept new connections and you will just need to read and write.
doRead(SelectionKey key){
//here we retrieve the key we attached earlier, so we now what to do / wheer the data is coming from
MyIdentifierType myIdentifier = (MyIdentifierType)key.attachment();
//This is then used to get back to the SocketChannel and Read the Data
myIdentifier.readTheData();
}
similarly for write
doWrite(SelectionKey key){
//here we retrieve the key we attached earlier, so we now what to do / wheer the data is coming from
MyIdentifierType myIdentifier = (MyIdentifierType)key.attachment();
//This is then used to get back to the SocketChannel and Read the Data
myIdentifier.getSocketHandler().writePendingData();
}
Reading is fairly straight forward, you just create a ByteBuffer and then call the SocketChannels read(ByteBuffer) (or one of its variants) to get the data ready on the channel until its empty.
Writing is a bit trickier as you will usually want to buffer the data to be written until you recieve the write event:
class MyNetworkClass{
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
SocketChannel commchannel; //from the server accept processing
...
public void write(byte[] data){
//here the class writeBuffer object is filled with the data
//but it isn't actually sent over the socket
...
}
public void writePendingData(){
//here actually write the data to the socket
commchannel.write(writeBuffer);
}
}
Note that you will need appropriate code to manage the buffer in the class in the event it becomes full, or to modify it appropriately in the write pending method if not all of the data in the buffer is written out to the socket, as well as the various exceptions that can be thrown during the process. Hope this helps to get you started.