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!
Related
I'm using a function to read bytes from non-blocking SocketChannel (socket from accept()) and from blocking SocketChannel (client side). I'm implementing a server using selector to handle multiple clients, and I'm using loopback address to use my laptop only. I wrote this
while((r = socketChannel.read(ackBuf)) != -1) {
System.out.println(name3d+" r: "+r);
}
and I expected that when the end of the content in the channel was reached, read() would returned -1 but is not what succedes.
read(), in non-blocking configuration, return 0 also if nothing is ready to read at the moment but it will be soon (if I understand well) so if I change the code to
while((r = socketChannel.read(ackBuf)) > 0) {
System.out.println(name3d+" r: "+r);
}
I will not read nothing also if something will be ready a moment later.
How can I distinguish if I got 0 because is not ready or because it is ended?
In the following snippet I can test for a second time the read after a sleep but I'm sure is not the reliable way to do what I want.
int times = 0;
while((r = socketChannel.read(ackBuf)) != -1 && times<2) {
if (r == 0)
try {
Thread.sleep(500);
times++;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name3d+" r: "+r);
}
" if I got 0 because is not ready or because it is ended?" Do you mean the message or the totality of the communication?
For the message, you should use a communication protocol (like json or http) for the communication, I think you should get a SocketException... You would if you using blocking and the person on the other end closed the connection... (I've written to a lot of people on SO about how SocketException is your friend)
--- edit ---
Looking over the documention for Channel, it looks like you should get an IOException of some kind (SocketException is a subclass of IOException) if/when the channcel is closed
The Non-blocking SocketChannel is used a bit different.
You first wait for the selection key to tell you that there is data, and
then you read that data from the channel.
See this code draft:
Selector selector = Selector.open();
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(addr);
sc.register(selector, SelectionKey.OP_READ);
while (true) {
// select() can block!
if (selector.select() == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(1024);
sc.read(bb);
System.out.println("Message received!");
}
}
Code for an echo server using a single threaded java I/O
public static void main(String[] args) throws Exception {
// create socket
int port = 4444;
ServerSocket serverSocket = new ServerSocket(port);
System.err.println("Started server on port " + port);
try {
// repeatedly wait for connections, and process
while (true) {
// a "blocking" call which waits until a connection is requested
Socket clientSocket = serverSocket.accept();
System.err.println("Accepted connection from client");
// open up IO streams
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintStream out = new PrintStream(clientSocket.getOutputStream());
// waits for data and reads it in until connection dies
// readLine() blocks until the server receives a new line from client
String s;
while ((s = in.readLine()) != null) {
out.print(s);
}
// close IO streams, then socket
System.err.println("Closing connection with client");
out.close();
in.close();
clientSocket.close();
}
} finally {
serverSocket.close();
}
}
code for the same using NIO
public static void main(String[] args) throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(PORT_NUMBER));
server.socket().setReuseAddress(true);
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (true) {
int channelCount = selector.select();
if (channelCount > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ, client.socket().getPort());
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
System.out.println("port: " + key.attachment());
if (client.read(buffer) < 0) {
key.cancel();
client.close();
} else {
buffer.flip(); // read from the buffer
/*
* byte[] received = new byte[buffer.remaining()];
* buffer.get(received); buffer.clear(); // write into the buffer
* buffer.put(received); buffer.flip(); // read from the buffer
*/
client.write(buffer);
buffer.clear(); // write into the buffer
}
}
}
}
}
}
Here using normal I/O the main thread comes and wait at the socket.accept() call. But NIO doesn't do that since socketChannel.accept() is not a blocking call.
So wont the NIO program be continuously running the loop ? And result in waste of CPU cycles ? Can I write the program in a better way. Sorry I am very new to JAVA NIO and asynchronous programming.
In normal IO the thread is blocked on serverSocket.accept().
With NIO the thread is blocked on selector.select().
From the JavaDoc of Selector#select():
This method performs a blocking selection operation.
Why is this called "non blocking IO"?
Actually, your first example (with normal IO) has two blocking calls: server.accept() and in.readLine().
Now consider the case with a badly behaving client: it opens a connection to the server, but never sends any data. With normal IO the server thread waits in in.readLine() for data to arrive and cannot serve any other client until the first client closes its connection.
With NIO the picture is different: if a client opens a connection, the server thread wakes up, server.accept()s the connection and registers the SocketChannel with the same selector. Then the server thread waits on the selector once more through selector.select(). Now there are two possibilities to wake up the server thread: either another client connecting, or the first client sending some data.
So the term "non blocking IO" does not mean that the server thread is never blocked - it means that a non-behaving client cannot block the server thread forever.
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.
This question already has an answer here:
Timeout on connect to socket using NIO SocketChannel [duplicate]
(1 answer)
Closed 8 years ago.
I am trying to understand how to write simplest application with one selector and multiple channels.
After incredible effort I was able to write following code:
server:
private static byte[] data = new byte[255];
public static void main(String[] args) throws IOException {
for (int i = 0; i < data.length; i++)
data[i] = (byte) i;
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(9000));
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
ByteBuffer source = ByteBuffer.wrap(data);
SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
key2.attach(source);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
if (!output.hasRemaining()) {
output.rewind();
}
client.write(output);
}
key.channel().close();
}
}
}
client(sender):
public static void main(String[] argv) throws Exception {
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
sChannel.connect(new InetSocketAddress("localhost", 9000));
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
buf.put((byte) 0xFF);
buf.flip();
int numBytesWritten = sChannel.write(buf);
}
my steps:
run server
run client
After this step in server console I see following:
Accepted connection from java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:49184]
In client console I see following:
Exception in thread "main" java.nio.channels.NotYetConnectedException
at sun.nio.ch.SocketChannelImpl.ensureWriteOpen(SocketChannelImpl.java:269)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:474)
at io.nio.SocketSender.main(SocketSender.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Please help to understand the reason of stack trace.
The message is clear enough. I would do the client connect in blocking mode: then it either completes or fails. Otherwise you have to use a Selector, select for OP_CONNECT, use finishConnect(), test its return value, etc. In fact I would use blocking mode through the client, and I probably wouldn't use NIO at all.
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