I have the following Java code:
class TestClient {
...
SocketChannel channel = SocketChannel.open()
channel.configureBlocking(false)
SelectionKey key = channel.register(clientSelector, SelectionKey.OP_CONNECT)
channel.connect(serverSocketAddress)
...
In the Selector thread I have the standard while loop and inside that:
if(key.isConnectable()) {
SocketChannel channel = (SocketChannel) key.channel();
channel.finishConnect(); <-- This is throwing NoConnectionPendingException rarely
}
Error message is: NoConnectionPendingException null
And this is spinning - see approx 100 messages like this.
Now, why would this be happening? If the connect fails, why would key.isConnectable() be true? Is this a race condition whereby the connect has failed. I cannot see any failure in the connect - no exception is thrown. Could it be a race condition where the connect is successful, then key.isConnectable is true, and somewhere between that and finishConnect() the connection has failed?
What can I do about it?
Related
I'm trying to develop my own communication library based on non-blocking NIO messages. I've been reading 1000 tutorials and book chapters about it and I think that at the end I have something that works with few simultaneous connections. But I'm having some issues when I have many connections coexisting on the server-side.
I have the typical selector implementation with the 4 private methods: accept, finishConnect, read and write. My problem lies on the first two ones: Accept and finishConnect.
When the client opens a new socket, and an acceptable key wakes the selector up, the following code is executed.
private void accept(SelectionKey key) {
try {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connexion completed");
changeInterest(sc, SelectionKey.OP_READ);
eventManager.addEvent(new ConnectionEstablished(sc));
} catch (Throwable e) {
NIOException ne = new NIOException(NIOException.ErrorType.ACCEPTING_CONNECTION, e);
eventManager.addEvent(new ErrorEvent(null, ne));
}
}
On the client side, I have this implementation for the connect method that will be invoked once the server processes its acceptable key for the socket.
private void finishConnect(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
try {
if (sc.finishConnect()) {
eventManager.addEvent(new ConnectionEstablished(sc));
LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connection finished");
} else {
LOGGER.debug("REFUSED " + sc + " - " + sc.socket().toString());
refusedConnection(sc, null);
key.cancel();
}
} catch (Exception e) {
refusedConnection(sc, e);
key.cancel();
}
}
The thing is that when I create some connections are accepted, the client executes the finishConnect message (and I can see the socket connection established with the ports used). But I can't find this connection acceptance on the server side, there is no connection completed log message using those ports!!
I suspected that an exception could arise between the ssc.accept() and the log invocation so I added some extra log messages to check which instruction was blowing everything up. The sequence was completed for all the keys that got into the accept method.
How is that possible if I can't even see any Error message on the log ?
EDIT: I made some tests on the number of sockets that are open at a time. When the client starts running, there's just one openSocket on the server: the server socket. After that it has up to 200 simultaneous open sockets, and at the end of the client execution the servers goes back to 1 open socket. I guess that they are never counted
By now, I've made a workaround that monitors the amount of coexisting connections on the node and delays new connections acceptance until that number is reduced to a given threshold. However I would like to understand what's going wrong on.
Thanks for your help.
Because of the backlog queue, it's perfectly in order for a large number of client connections to complete before accept() is executed. So there is no actual problem here to solve.
But are you ever executing the accept() method? This is the bug you need to investigate.
As EJP suggested the problem was on the backlog queue. I was binding the ServerSocketChannel using the bind(SocketAddress local) method.
When a socket request arrives to the JVM it is enqueued to the backlog queue and waits there until the Listener triggers the process of the corresponding key to be accepted. The actual problem lies on the size of this queue, using the bind method, it stores up to 50 connections.
When a peak of connection requests happens, there's an overflow on the queue and some of them are lost. To avoid this happening, the method bind(SocketAddress local, int backlog), allows to change the capacity of the queue and increase it.
On the other side, when working in non-blocking mode, the selector on the client node does not need the connection to be accepted to process a OP_CONNECT key. The reception of the SYN-ACK TCP message will trigger the corresponding key in the selector.
I'm trying to connect to a remote server and send a login message in my Thread:
#Override
public void run() {
try {
address = new InetSocketAddress(host, port);
incomingMessageSelector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(address);
socketChannel.register(incomingMessageSelector, SelectionKey.OP_READ);
serverManager.loginToServer();
}
}
the loginServer() is a method which send a message to ther server but i keep getting an:
java.nio.channels.NotYetConnectedException
how can i check and wait for connection before sending this loginServer() method?
If you're connecting in non-blocking mode you should:
register the channel for OP_CONNECT
when it fires call finishConnect()
if that returns true, deregister OP_CONNECT and register OP_READ or OP_WRITE depending on what you want to do next
if it returns false, do nothing, keep selecting
if either connect() or finishConnect() throws an exception, close the channel and try again or forget about it or tell the user or whatever is appropriate.
If you don't want to do anything until the channel connects, do the connect in blocking mode and go into non-blocking mode when the connect succeeds.
i've found an answer.. i should use:
socketChannel = SocketChannel.open(address);
socketChannel.configureBlocking(false);
while (!socketChannel.finishConnect());
//my code after connection
because the NIO is in not blocking mode we have to wait until it finish its connection
I have tried writing to the response, because there is no proper disconnect callback:
private boolean write(byte[] output, AsyncContext context) {
try {
OutputStream stream = context.getResponse().getOutputStream();
stream.write(output);
stream.flush();
return true;
} catch (IOException ex) {
//client disconnected
log.error(ex);
removeAsyncContext(context);
return false;
}
}
But this didn't seem to the trick. When the client is disconnected, writing and flushing the buffer did not throw an exception.
The weird thing is, the second time you try to write to the output stream (after the disconnect), the write does throw an exception. It looks like the first time you write/flush it,some internal state is set to error, without notifying.
I have tried on both Jetty 8 and Tomcat 7 and I see the same behavior.
Is there a solution to find out whether the message is received by the client? Am I missing something?
I recognize you are looking for a proper way of detecting disconnects, but for those who don't mind a kludge:
Note: This method periodically sends space characters to the client that must be trimmed before interpreting the results. This is the kludgey part.
Start a thread that has access to the writer/outputstream of the servlet response. This thread sends space characters periodically (I used 1 second intervals) to the client. Wrap in a IOException try/catch block that sets your abort flag.
If the connection is closed, most servlets will throw a flavor of EOFException (which is an IOException) when data cannot be delivered to the client. You are catching this exception and setting your abort flag.
When the abort flag is caught, you have options. You can nuke the executing thread, have all your processing periodically check for the abort flag, push an exception into the executing thread, or do any number of things (not detailed here).
Should the process finish successfully, you will get your results prefixed by a number of spaces. Again, remember to trim these on your client.
In my experience when a client disconnects from a servlet there is an exception referring to a Broken Pipe.
For example: Broken Pipe when writing bytes in ServletOutputStream
I would suggest catching java.net.SocketException and looking at the exception details to verify if it is a broken pipe (as a starting point):
Caused by: ClientAbortException: java.net.SocketException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:358)
at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:354)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:381)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:370)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
im trying to implement a client app with an asynchronous connection. i want to know if i can reuse a SocketChannel object after it has failed to connect to a server.
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
InetSocketAddress addr = new InetSocketAddress(host, port);
SelectionKey key = channel.register(select, SelectionKey.OP_READ, connection);
channel.connect(addr);
after this is the select loop, my socket eventually gets selected because the connection failed. i would like to queue another connection attempt on that channel, and nothing i do seems to do it. the channel.isConnectionPending() method always returns true (even if i try to finishConnect)
is the only solution do get rid of this SocketChannel and create a new one?
Sun has released the Java 6.0 sources under the GPL. "Read the source Luke"
And my reading of the code is that you cannot call connect a second time.
Hmmm... I'm no NIO expert but something looks fishy. You are registering for OP_ACCEPT, which occurs when a listening channel receives an incoming connection -- however, you're using the channel for an outgoing connection.
You should post more of the code, including the select loop.
Try it and see?
I'm not 100% sure, but I suspect you can once finishConnect has been called and returned or an exception from it thrown it may be ok.
Put a try/catch block around the connect and when the exception is caught you may be safe to call the channel.connect method again.
If it doesn't work then the answer is probably no.
When I code like this:
ServerSocketChannel ssc = ServerSocketChannel.open();
InetSocketAddress sa = new InetSocketAddress("localhost",8888);
ssc.socket().bind(sa);
ssc.configureBlocking(false);
ssc.socket().accept();
the ServerSocket.accept() method throws java.nio.channels.IllegalBlockingModeException. Why can't I call accept(), even though I set blocking to false?
The problem is that you are calling ssc.socket().accept(), not ssc.accept(). If you change the last line to ssc.accept() then it will work as expected, which is to return a SocketChannel if one is waiting or null if not.
Because that's what javadoc for serversocket.accept() says?
IllegalBlockingModeException - if this socket has an associated channel, and the channel is in non-blocking mode.
The Javadoc specifically states that ServerSocketChannel.accept():
Accepts a connection made to this channel's socket.
If this channel is in non-blocking mode then this method will immediately
return null if there are no pending connections. Otherwise it will block
indefinitely until a new connection is available or an I/O error occurs.
The general idea is:
If you want to block while waiting for incoming connections, you leave the server socket in blocking mode. If you're writing a server that has nothing to do until a connection actually comes in, then blocking mode is what you want.
If you want to do other things, and periodically check to see whether there's a pending connection, you want non-blocking mode.
Blocking mode is the default for a reason: Most servers don't want to poll their accepting socket for incoming connections.