How java-nio is Non Blocking confused - java

I am new to NIO i understand the concept of Asynchronous Socket but i am confused on Non Blocking part.
I am using java NIO Selector . My Code for Server is
public class EcoNonBlockingIOServer_7 {
public static int PORT_NUMBER = 5555;
public static void main(String[] argv) throws Exception {
new EcoNonBlockingIOServer_7().go(argv);
}
public void go(String[] argv) throws Exception {
int port = PORT_NUMBER;
if (argv.length > 0) { // Override default listen port
port = Integer.parseInt(argv[0]);
}
System.out.println("Listening on port " + port);
// Allocate an unbound server socket channel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// Get the associated ServerSocket to bind it with
ServerSocket serverSocket = serverChannel.socket();
// Create a new Selector for use below
Selector selector = Selector.open();
// Set the port the server channel will listen to
serverSocket.bind(new InetSocketAddress(port));
// Set nonblocking mode for the listening socket
serverChannel.configureBlocking(false);
// Register the ServerSocketChannel with the Selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// This may block for a long time. Upon returning, the
// selected set contains keys of the ready channels.
int n = selector.select();
if (n == 0) {
continue; // nothing to do
}
// Get an iterator over the set of selected keys
Iterator it = selector.selectedKeys().iterator();
// Look at each key in the selected set
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
// Is a new connection coming in?
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
sayHello(channel);
}
// Is there data to read on this channel?
if (key.isReadable()) {
readDataFromSocket(key);
}
// Remove key from selected set; it's been handled
it.remove();
}
}
}
Now My Queries are:
If we register a channel with selector on any operation it always get blocked on selector.select() then how it is non blocking.
If we admit it uses OP_ACCEPT as key and maps channel accordingly but again In key is acceptable i am modifying this channel selector to OP_READ since it already has been accepted. Again It blocks on selector.select() for read event
*Please correct my understanding if i am wrong *

If we register a channel with selector on any operation it always get blocked on selector.select() then how it is non blocking.
select() is blocking. Every operation on a non-blocking channel itself is non-blocking, i.e. read() and write().
If we admit it uses OP_ACCEPT as key and maps channel accordingly but again In key is acceptable i am modifying this channel selector to OP_READ since it already has been accepted.
Very confused. The channel whose interest-ops == OP_ACCEPT is the listening socket. The channel you accepted from the listening socket is a connected socket, and it is this socket that you put into non-blocking mode, register with OP_ACCEPT, etc.
Again It blocks on selector.select() for read event
Correct, but it doesn't block in read() or write() or accept() or finishConnect(). Using a selector is actually called multiplexed I/O: you wait for multiple channels and multiple events at the same time in a single operation.

Related

Java NIO closing one channel resulting the other channel not responding

I just found that the problem might due to multi-threading problem. After the read() method I sent the request to several worker threads to process the data. When the thread pool size is 1, the problem doesn't occur. However, when the thread pool size is bigger than 1, this problem occurs.
I initialized my threadpool before the while(true) loop, and execute my worker thread after the read() method.
I'm using Java NIO in a middleware that connects a memtier client and a memcached server. The system works fine with only one clients connected, however, when there are more clients connected, when one client finishes and closes its channel, the other client won't be able to read or write to its channel any more.
My implementation looks like the following:
public void run() {
try{
// Connect to client
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress(8000);
server.socket().bind(address);
server.register(selector,SelectionKey.OP_ACCEPT);
// Connect to Server
Socket socket = new Socket("localhost",8090);
// Select keys
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
// check if key is valid
if(!key.isValid()) {
continue;
}
if(key.isAcceptable()) {
accept(key);
}else if(key.isReadable()) {
read(key);
}
}
}
}
}
My accept() and read() looks like this:
private void accept(SelectionKey key) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
buffer.clear();
int n = -1;
n = channel.read(buffer);
if(n == -1) {
key.cancel();
channel.close();
}else{
//read to buffer
}
}
I have tried many ways but still cannot find out the problem. Thank you very much!

How to disconnect a client from NIO server socket

I am developing a server that is connected with many clients. I need to know when a client is disconnecting from server. So each client is sending a specific character to the server. If the character is not received after two seconds then I should disconnect the server from the client (releasing allocated resource for this client).
This is the main code of my server:
public EchoServer(int port) throws IOException {
this.port = port;
hostAddress = InetAddress.getByName("127.0.0.1");
selector = initSelector();
loop();
}
private Selector initSelector() throws IOException {
Selector socketSelector = SelectorProvider.provider().openSelector();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(hostAddress, port);
serverChannel.socket().bind(isa);
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
return socketSelector;
}
private void loop() {
for (;true;) {
try {
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
.iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
// Check what event is available and deal with it
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if (key.isWritable()) {
write(key);
}
}
Thread.sleep(1000);
timestamp++;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
The first question is that, whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
If it is good, how can I detect with SelectionKey is related to witch client and then how can I disconnect the key from server?
The first question is that, whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
Not in the case of an echo server. In many cases such as this, all you need is to recognize end of stream and connection failure appropriately.
how can I detect with SelectionKey is related to which client
The SelectionKey has a channel, the channel has a socket, and the Socket has a remote IP address:port. That's all you need.
and then how can I disconnect the key from server?
Close the channel when you get -1 from the read() method, or any IOException when reading or writing.
whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
Yes, it is called a heartbeat.
how can I detect with SelectionKey is related to witch client and then how can I disconnect the key from server?
You can attach an object which has all the information need regarding a channel. You include this when you register the channel.

In JAVA nio selector, when should I register the 'write operation'?

I am learning Java nio selector. In my understanding, I thought the steps using selector is to firstly I register the my interest operations and then I can check the ready set and finally I can do the operations corresponding my interest operations. I don't know why in this code the writing process can happen in the block of if (key.isReadable()){...} but not if (key.isWritable){...} and why writing operation is not registered?
Iterator keys = sel.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey)keys.next();
if (!key.isValid())
continue;
if (key.isAcceptable()) {
// increase the counter
connection++;
// remove accept request
keys.remove();
// ACCEPT: get the server channel
ServerSocketChannel ssc =
(ServerSocketChannel) key.channel();
// init a socket for a client
SocketChannel nsc = ssc.accept();
nsc.configureBlocking(false);
// register the socket for READ
nsc.register(sel, SelectionKey.OP_READ);
}
}
while (count < COUNT_MAX + NUM_CHILD - 1) {
sel.select();
// Get all pending events and iterate
Iterator keys = sel.selectedKeys().iterator();
while ( keys.hasNext() ) {
SelectionKey key = (SelectionKey)keys.next();
keys.remove();
if (!key.isValid())
continue;
if (key.isReadable()) {
// READ: get the channel
SocketChannel nsc = (SocketChannel) key.channel();
// clear buffer for reading
readBuffer.clear();
int nBytes = nsc.read(readBuffer);
if (nBytes == -1) {// Check if the client closed the socket
key.channel().close();
key.cancel();
continue;
}
// Read a message
DataInputStream ist = new DataInputStream (
new ByteArrayInputStream(readBuffer.array()));
String msg = ist.readUTF();
System.out.print(msg + "\n");
// Clear the write buffer
writeBuffer.clear();
// Write the counter value on the buffer
count++;
if (count < COUNT_MAX)
writeBuffer.putInt(count);
else
writeBuffer.putInt(-1);
// flip the buffer and write on the channel
writeBuffer.flip();
// Reply to a client
nsc.write(writeBuffer);
}
} // while (key)
You don't need to register interest in OP_WRITE because usually the channel is ready for writing. However a WritableChannel, if in non blocking mode, might not succeed in writing all content of the given ByteBuffer. See here in its java docs:
Some types of channels,
depending upon their state, may write only some of the bytes or
possibly none at all. A socket channel in non-blocking mode, for
example, cannot write any more bytes than are free in the socket's
output buffer.
In this case you need to register the interest for OP_WRITE on the selector to be notified when the channel is once again ready for writing, so you can finish writing your ByteBuffer.
See here a related SO question.

Muitiplexed socket communication in Java

I am writing a server program that can accept communication from multiple (but fixed) number of clients. I want to keep the program single-threaded. To do so, I am using non-blocking socket to iterate over each client, but each client's channel uses blocking mode. Here's my server code:
class server {
public static void main(String args[])
throws Exception {
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new java.net.InetSocketAddress("localhost", 8005));
System.out.println("Server attivo porta 8005");
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
for(;;) {
selector.select();
Set keys = selector.selectedKeys();
Iterator i = keys.iterator();
while(i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
i.remove();
if (key.isAcceptable()) {
SocketChannel client = channel.accept();
client.configureBlocking(true);
ObjectInputStream ois = new ObjectInputStream(
client.socket().getInputStream());
String s = (String)ois.readObject();
System.out.println(s);
}
}
}
}
}
The client uses simple blocking I/O, as shown here:
class client {
public static void main(String args[]) throws Exception {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(true);
channel.connect(new java.net.InetSocketAddress("localhost", 8005));
ObjectOutputStream oos = new ObjectOutputStream
(channel.socket().getOutputStream());
for (int i = 0; i < 100; i++) {
oos.writeObject(new String("Hello " + i));
System.out.println(i);
}
}
}
The problem is that although the client wants to write 100 times, the server is reading the message just once. Neither the server nor the client is giving any exception, but I am only getting the output "Hello 0" from the server. Is there any problem in what I am doing here? If so, what alternatives do I have?
Thanks.
Update: Closing the ObjectInputStream within the server's loop gives a BrokenPipeException by the client (the server behaves in the same way).
The issue is that you're just checking for new connections with key.isAcceptable(). You also need to check for reads with key.isReadble(). You should only be doing connection setup from key.isAcceptable().
See Java ServerSocketChannel SocketChannel (Callback)
The problem is that the server is not waiting for the client to send all it's data. In client server programs, what you need to do is to establish a clear protocol between both so that they are in sync when data is transmitted/received. This is usually done by signaling an end of transmission by either side by sending a designated symbol or closing the connection when they are done

IP address of clients in socket using NIO

Using NIO, we have bound two ports to ServerSocket class.
serverChannelPrimary = ServerSocketChannel.open();
serverChannelSecondary = ServerSocketChannel.open();
// Retrieves a server socket associated with this channel
serverSocketPrimary = serverChannelPrimary.socket();
serverSocketSecondary = serverChannelSecondary.socket();
// Opens a connection selector
connectionSelector = Selector.open();
// Bind the specified port num
serverSocketPrimary.bind(new InetSocketAddress(portOne));
serverSocketSecondary.bind(new InetSocketAddress(portTwo));
// Set nonblocking mode for the listening socket
serverChannelPrimary.configureBlocking(false);
serverChannelSecondary.configureBlocking(false);
// Register the ServerSocketChannel with the Selector
serverChannelPrimary.register(connectionSelector, SelectionKey.OP_ACCEPT);
serverChannelSecondary.register(connectionSelector, SelectionKey.OP_ACCEPT);
Now, we are also able to fetch the IP address of the clients that are connected when the the new client makes the first request, which we are adding to a vector clientIps.
while (isActive) {
try {
numberOfKeys = 0;
numberOfKeys = connectionSelector.select(timeOut);
if (numberOfKeys == 0) {
continue; // None of request available
}
// Get iterator through the selected keys list
Iterator<SelectionKey> iterKeys = connectionSelector
.selectedKeys().iterator();
while (iterKeys.hasNext()) {
try {
SelectionKey selectedKey = (SelectionKey) iterKeys
.next();
// Verify the key validity
if (!selectedKey.isValid()) {
logger.error("Received key is invalid");
continue;
} else if (selectedKey.isAcceptable()) {
// Accept the client request
ServerSocketChannel server = (ServerSocketChannel) selectedKey
.channel();
SocketChannel channel = server.accept();
// Get the socket associated with this channel
Socket clientInfo = channel.socket();
logger.debug("Application got client request from (Host name:"
+ clientInfo.getInetAddress().getHostName()
+ ",Ip address:"
+ clientInfo.getInetAddress()
.getHostAddress()
+ ",port:"
+ clientInfo.getPort());
String clientAddress=clientInfo.getInetAddress().getHostAddress();
if(!clientIps.contains(clientAddress)){
clientIps.add(clientAddress);
}
logger.debug("List of client : "+clientIps);
clientMgr.includeClient(channel);
}
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
logger.debug("Since this key has been handled, remove the SelectedKey from the selector list.");
iterKeys.remove();
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
However, after the connection has been made, once we start getting data from multiple clients on both the ports, is it possible to determine, the IP address of each client whenever each client sends the data. I hope the code that I have provided is sufficient and clear to explain the situation we are having.
ServerSocketChannel is TCP, so the IP addresses at the two ends can't change.
In your line
SocketChannel channel = server.accept();
channel is specific to a particular client. These are the objects you will be using to communicate with each client and each one represents a single TCP session with a single remote ip/port tuple.
You can call SocketChannel.socket().getSocketAddress() to get the remote address of any specific SocketChannel.
Once you get the socketChannel to be able to send back to client, you get use the functions below.
//Not complete example
SocketChannel ssc;
/* after accepting and other such required operations */
ssc.socket().getInetAddress().toString();
/**
Returns:
the remote IP address to which this socket is connected, or null if the socket is not connected.
will return 10.50.10.20 as a string
*/
//To get remote port as an int
ssc.socket().getPort();
I do not see "reading" part of code, but I am sure you have one. You can try to get remote socket address (ip + port) like this:
if (selectionKey.isReadable()) {
SocketChannel client = (SocketChannel) selectionKey.channel();
// you can here read data from given socket; client.read(buffer);
// and also get remote (and local too) address
client.getRemoteAddress();
}

Categories

Resources