I use SocketChannel to receive TCP stream from server on client side. For example:
Selector selector=Selector.open();
SocketChannel mychannel=SocketChannel.open(new InetSocketAddress(ip,port));
channel.configureBlocking(false);
SelectionKey channelkey=channel.register(selector,SelectionKey.OP_READ);
Then, I can use the selector.select() method to handle reading problem.
while(true){
if(selector.select()!=0){
Iterator<SelectionKey> it=selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key=it.next();
it.remove();
if(key.isReadable()){
if(key.equals(channelkey)){
//concrete handle
...
}
}
}
}
}
With concrete handle,considering I'd like to use InputStream(I wanna read stream line) to receive tcp stream from server side,there are two methods.One is using channel.socket(), another is to use Channels. Here I use channel.socket(), for example:
SocketChannel channel = (SocketChannel) key.channel();
key.cancel();
channel.configureBlocking(true);
InputStream ins = Channels.newInputStream(channel);
InputStreamReader is = new InputStreamReader(ins,"utf-8");
BufferedReader in = new BufferedReader(is);
String res = in.readLine();
while (!res.equals("")) {
System.out.println(res);
res = in.readLine();
}
......①
OK, now I finish reading tcp stream for one time.In order to continue to using selector, I should set channel blocking mode to false.
channel.configureBlocking(false);
The question is,the key that combines channel and selector has been canceled.I want to register mychannel again.What should I do? It seems that if I use mychannel.register(selector, SelectionKey.OP_READ) again on ①, it throws Exception.
My run() method code is as follows:
try {
if (selector.select(getTimeout()) != 0) {
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
key.cancel();
channel.configureBlocking(true);
InputStream ins = Channels.newInputStream(channel);
InputStreamReader is = new InputStreamReader(ins,"utf-8");
BufferedReader in = new BufferedReader(is);
String res = in.readLine();
while (!res.equals("")) {
System.out.println("========" + res);
res = in.readLine();
}
channel.configureBlocking(false);
System.out.println(key.isValid());
proxykey=channel.register(selector, SelectionKey.OP_READ);
}
it.remove();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
The Exception it throws is:
Exception in thread "Thread-0" java.nio.channels.CancelledKeyException
at sun.nio.ch.SelectionKeyImpl.ensureValid(Unknown Source)
at sun.nio.ch.SelectionKeyImpl.interestOps(Unknown Source)
at java.nio.channels.spi.AbstractSelectableChannel.register(Unknown Source)
at java.nio.channels.SelectableChannel.register(Unknown Source)
at AgentThread.run(AgentThread.java:185)
SelectionKey.cancel() doesn't take full effect until the next select(), for various arcane reasons. You could try calling selectNow() after the cancel, or maybe better just before the re-registration.
Related
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!
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 can send data from io client(using just socket not socket channel), but it was not possible to send data from nio server(using socket channel). What is the problem?
Is there a difference between the way to read/write through I/O stream and bytebuffer?
public class Server {
// this is equivalent to the server socket in the non nio world
ServerSocketChannel serverSocketChannel;
// this is the multiplexer which multiplexes the messages received from
// different clients
Selector selector;
public Server() {
try {
// get a selector
selector = Selector.open();
// get a server socket channel
serverSocketChannel = ServerSocketChannel.open();
// we force the socket to be Non-blocking.
// if it is set to "true" then this socket acts as a normal
// (blocking) server socket
serverSocketChannel.configureBlocking(false);
// port and ip address where the server listens for connections
InetSocketAddress add = new InetSocketAddress(
InetAddress.getLocalHost(), 9999);
// bind the server socket to the ip/port
serverSocketChannel.socket().bind(add);
// register the serverSocketChannel (for incoming connection events)
// to the selector.
// The "SelectionKey.OP_ACCEPT" parameter tells the selector that
// this serverSocketChannel registers
// itself for incoming (acceptable) connections
SelectionKey key = serverSocketChannel.register(selector,
SelectionKey.OP_ACCEPT);
System.out.println("serverSocketChannel's registered key is : "
+ key.channel().toString());
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.startListening();
}
private void startListening() {
System.out.println("Server is listening on: "
+ serverSocketChannel.socket().getInetAddress()
.getHostAddress() + ":"
+ serverSocketChannel.socket().getLocalPort());
while (true) {
try {
// this line blocks until some events has occurred in the
// underlying socket
selector.select();
// get the selected keys set
Set selectedKeys = selector.selectedKeys();
Iterator iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
// a client has asked for a new connection
if (key.isAcceptable()) {
// only ServerSocketsChannels registered for OP_ACCEPT
// are excepted to receive an
// "acceptable" key
System.out.println("Key ready to perform accept() : "
+ key.channel().toString());
// as usual the accept returns the plain socket towards
// the client
SocketChannel client = serverSocketChannel.accept();
// set the client socket to be non blocking
client.configureBlocking(false);
// register the client socket with the same selector to
// which we have registered the
// serverSocketChannel
client.register(selector, SelectionKey.OP_READ);
// client.register(selector, SelectionKey.OP_WRITE);
continue;
}
// the client has sent something to be read by this server
if (key.isReadable()) {
System.out.println("Key ready to perform read() : "
+ key.channel().toString());
// get the underlying socket
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(10000);
// read the msg sent by the client
client.read(bb);
// display the message
bb.flip();
byte[] array = new byte[bb.limit()];
bb.get(array);
System.out.println(new String(array));
// send the message
ByteBuffer bb1 = ByteBuffer.allocate(10000);
String s = "server data";
byte[] array1 = new byte[bb1.limit()];
array1 = s.getBytes();
bb1.put(array1);
client.write(bb1);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
void write() {
}
}
public class Client {
static String IP = "192.168.123.105"; // server IP address
public static void main(String[] args) throws UnknownHostException,
IOException {
// TODO Auto-generated method stub
String msg;
Socket socket = new Socket(IP, 9999);
System.out.println("client: complete making socket");
Scanner scan = new Scanner(System.in);
System.out.println("client: input data to send");
while (true) {
// read
System.out.print(">>>");
msg = scan.nextLine();
OutputStream out = socket.getOutputStream();
DataOutputStream dou = new DataOutputStream(out);
dou.writeUTF(msg);
// write from server
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
String remsg = din.readUTF();
System.out.println("client: data from server" + remsg);
if (remsg.equalsIgnoreCase("END")) {
System.out.println("SOCKET END");
socket.close();
break;
}
}
}
}
i solved problem!
it was a problem about how to read from inputstream in client
i changed a part of client code
from
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
String remsg = din.readUTF();
System.out.println("client: data from server" + remsg);
to
ByteBuffer bb1 = ByteBuffer.allocate(10000);
byte[] array1 = new byte[bb1.limit()];
InputStream in = socket.getInputStream();
in.read(array1);
String remsg=new String(array1);
and also added OP_WRITE/isWritable() in server code like this
if (key.isReadable()) {
System.out.println("Key ready to perform read() : "
+ key.channel().toString());
// get the underlying socket
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(1024);
// read the msg sent by the client
client.read(bb);
// display the message
bb.flip();
byte[] array = new byte[bb.limit()];
bb.get(array);
System.out.println(new String(array));
client.register(selector, SelectionKey.OP_WRITE);
continue;
}
if(key.isWritable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb1 = ByteBuffer.allocate(10000);
String s = "server data";
byte[] array1 = new byte[bb1.limit()];
array1 = s.getBytes();
bb1.put(array1);
bb1.flip();
client.write(bb1);
}
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 converting some Java NIO code to run in Scala and I am getting an error because the SelectionKey I'm calling returns a SelectableChannel rather than a DatagramChannel, which is a subclass of SelectableChannel and an instance of which I declare at the beginning of the code. I did not come to Scala from Java, so my knowledge of Java is actually very limited. It seems to me that the Java code DatagramChannel channel = (DatagramChannel) key.channel(); typecasts the channel to a DatagramChannel. Is this what I need to do in the Scala code?
Scala code:
val channel = DatagramChannel.open()
val selector = Selector.open()
println("Attempting to bind to socket " + port)
channel.socket().bind(new InetSocketAddress(port))
println("Bound to socket " + port)
channel.configureBlocking(isBlocking)
println("Attempting to registered selector")
channel.register(selector, SelectionKey.OP_READ)
println("Registered selector")
println("Ready to receive data!");
while (true) {
try {
while(selector.select() > 0) {
val keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
val key = keyIterator.next();
if (key.isReadable()) {
val channel = key.channel(); // FIXME: returning a SelectableChannel instead of a DatgramChannel
var buffer: Array[Byte] = Array();
val byteBuffer = ByteBuffer.wrap(buffer);
val sockAddress = channel.receive(byteBuffer);
// ...
Original Java code:
channel = DatagramChannel.open();
selector = Selector.open();
System.out.println("Attempting to bind to socket " + port);
channel.socket().bind(new InetSocketAddress(port));
System.out.println("Bound to socket " + port);
channel.configureBlocking(isBlocking);
System.out.println("Attempting to registered selector");
channel.register(selector, SelectionKey.OP_READ);
System.out.println("Registered selector");
System.out.println("Ready to receive data!");
while (true) {
try {
while(selector.select() > 0) {
Iterator keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if (key.isReadable()) {
DatagramChannel channel = (DatagramChannel) key.channel();
byte[] buffer = new byte[2048];
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
SocketAddress sockAddress = channel.receive(byteBuffer);
// ...
SelectionKey.channel() always returns a SelectableChannel. The assigned type of the channel is not really relevant at this point, so you'll have to cast it.