The simplest NIO-server example - java

I'm trying to run the simplest NIO-server which just accepts connections.
public static void main(String[] args) throws IOException{
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress("localhost", 1456));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
try {
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
if (key.isAcceptable())
accept(key, selector);
}
} catch (IOException e) {
System.err.println("I/O exception occurred");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void accept(SelectionKey key, Selector selector) throws IOException{
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false); //<------- NPE Here
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.register(selector, SelectionKey.OP_READ);
}
And the simplest I/O client:
public static void main(String[] ars) throws IOException{
Socket s = new Socket("localhost", 1456);
OutputStream ous = s.getOutputStream();
InputStream is = s.getInputStream();
while (true) {
ous.write(new byte[]{1, 2, 3, 3, 4, 5, 7, 1, 2, 4, 5, 6, 7, 8});
is.read();
}
}
When I run both this processes I get a bunch of NullPointterExceeptions.
When the client connects for the first time, it's okay. We retrieve the key, get the channel and accept the incoming connection.
But the problem is for some unclear to me reason I keep retrieving the key that is acceptable and try to accept more. The SocketChannel channel = serverChannel.accept(); is null and I get NPE.
But why am I always notified with the key that is accepted? What did I do wrong?

You need to remove each SelectionKey from the selected set after you process it. Otherwise you will get the same event again, even though it isn't really ready: so for example accept() will return null.
You need to look at a tutorial. No good just making it up. See the Oracle NIO tutorial.

Related

Why a key has to be removed from selectedKeys

This is a simple server, I used nc as clients to connect to the server, the first client went through and entered the acceptHandler. However, the second client cannot trigger the select to return a number greater than 0 (the return value of select is 0). I see that removing the processed key will resolve the issue, what I don't understand is that why a new connection cannot trigger an event when the serverSocket is still registered with the selector
static ServerSocketChannel server;
static Selector selector;
public static void main(String[] args) throws IOException {
server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(9090));
server.configureBlocking(false);
selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int num;
while ((num = selector.select()) > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectionKeys.iterator();
while (iter.hasNext()) {
SelectionKey sk = iter.next();
// iter.remove();
if (sk.isAcceptable()) {
acceptHandler(sk);
} else if (sk.isReadable()) {
readHandler(sk);
}
}
}
}
}
public static void acceptHandler(SelectionKey sk) {
System.out.println("accept handle");
ServerSocketChannel server = (ServerSocketChannel) sk.channel();
SocketChannel client = null;
try {
client = server.accept(); // return null if no pending connections
if (client != null) {
client.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(65535);
client.register(selector, SelectionKey.OP_READ, buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}

How can I handle multipart form data in Java NIO Htttp Server

I have implemented a Non Blocking Htttp Server by using Java NIO. It works fine for x-www-form-urlencoded POST requests. But when i try it for a HTTP multipart request with large file, it is not working. In that situation Server unable to make response to http client. This is my source code for the NIO Server.
public class TCPServer {
public static void main(String[] args) {
TCPServer server = new TCPServer();
server.listen();
}
public void listen() {
try {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel
.open();
InetSocketAddress serverAddress = new InetSocketAddress(8080);
serverSocketChannel.bind(serverAddress);
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
SocketChannel clientSocketChannel = serverSocketChannel.accept();
clientSocketChannel.configureBlocking(false);
clientSocketChannel.register(selector,
SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel clientSocketChannel = null;
try {
clientSocketChannel = (SocketChannel) key.channel();
ByteBuffer clientBuffer = ByteBuffer.allocate(1024);
StringBuilder requestStringBuilder = new StringBuilder();
int bytesRead = clientSocketChannel
.read(clientBuffer);
while (bytesRead > 0) {
clientBuffer.flip();
String result = new String(clientBuffer.array());
requestStringBuilder.append(result);
clientBuffer.compact();
bytesRead = clientSocketChannel.read(clientBuffer);
}
System.out.println("request-----");
System.out
.println(requestStringBuilder.toString());
clientSocketChannel.write(ByteBuffer
.wrap("reply from server".getBytes()));
clientSocketChannel.register(selector,
SelectionKey.OP_WRITE);
} catch (Exception e) {
System.err.println(e.getMessage());
clientSocketChannel.close();
}
} else if (key.isWritable()) {
SocketChannel clientSocketChannel = null;
try {
clientSocketChannel = (SocketChannel) key.channel();
clientSocketChannel.close();
} catch (Exception e) {
System.err.println(e.getMessage());
clientSocketChannel.close();
}
}
iterator.remove();
}
}
}catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
Is there any other way to handle HTTP multipart request inside Java NIO non blocking server.How can i fix this. Thanks.
This is similar to another question: Servlet 3.1 - Multipart async processing, but I'll answer here as the solution works with plain non-blocking IO too.
Synchronoss Technologies recently open sourced a non-blocking HTTP multipart parser here.
As your non-blocking server receives data, you just need to pass the incoming bytes to the NioMultipartParser. The parser will make callbacks to your code for each of the parts received.
Disclaimer: I work for Synchronoss Technologies. We wrote this for Servlet 3.1 but it should intentionally work in regular non-blocking applications too, so hopefully others will find this library useful.

Not able to get the output in NIO

I want the program to give me an echo back when i type something, i don't get any errors but it doesn't work, it connects properly but i don't receive anything back when i type in the server
private static Selector selector;
public static void main(String[] args) throws IOException {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress("localhost", 8080));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
for (Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext();) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable())
acceptRead(key);
else if (key.isWritable())
write(key);
}
}
}
private static void acceptRead(SelectionKey key) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
System.out.println("Connected with " + sc);
sc.configureBlocking(false);
SelectionKey key2 = sc.register(selector, SelectionKey.OP_WRITE);
ByteBuffer buf = ByteBuffer.allocate(32);
sc.read(buf);
buf.flip();
key2.attach(buf);
}
private static void write(SelectionKey key) throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
sc.write(buf);
}
}
Very strange code here.
If the key is acceptable you should call accept() on the channel.
If the key is readable you should call read() on the channel.
If you get -1 from read() you must close the channel.
After you write you must compact() the buffer, and surely you want to register the channel for OP_READ again?

Non-Blocking IO in java with logic

I am creating a server that will handle >1000 connections. I decided to go with non-blocking IO in my server. I found some code on the internet, which is basically an echo server. I think everything is fine, but I don't understand a few of the concepts in the server.
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
public class EchoServer {
private InetAddress addr;
private int port;
private Selector selector;
private Map<SocketChannel,List<byte[]>> dataMap;
public EchoServer(InetAddress addr, int port) throws IOException {
this.addr = addr;
this.port = port;
dataMap = new HashMap<SocketChannel,List<byte[]>>();
startServer();
}
private void startServer() throws IOException {
// create selector and channel
this.selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// bind to port
InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
serverChannel.socket().bind(listenAddr);
serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
log("Echo server ready. Ctrl-C to stop.");
// processing
while (true) {
// wait for events
this.selector.select();
// wakeup to work on selected keys
Iterator keys = this.selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
// this is necessary to prevent the same key from coming up
// again the next time around.
keys.remove();
if (! key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
}
else if (key.isReadable()) {
this.read(key);
}
else if (key.isWritable()) {
this.write(key);
}
}
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
// write welcome message
channel.write(ByteBuffer.wrap("Welcome, this is the echo server\r\n".getBytes("US- ASCII")));
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr);
// register channel with selector for further IO
dataMap.put(channel, new ArrayList<byte[]>());
channel.register(this.selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(8192);
int numRead = -1;
try {
numRead = channel.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
}
if (numRead == -1) {
this.dataMap.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr);
channel.close();
key.cancel();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
log("Got: " + new String(data, "US-ASCII"));
// write back to client
doEcho(key, data);
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
Iterator<byte[]> items = pendingData.iterator();
while (items.hasNext()) {
byte[] item = items.next();
items.remove();
channel.write(ByteBuffer.wrap(item));
}
key.interestOps(SelectionKey.OP_READ);
}
private void doEcho(SelectionKey key, byte[] data) {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
pendingData.add(data);
key.interestOps(SelectionKey.OP_WRITE);
}
private static void log(String s) {
System.out.println(s);
}
public static void main(String[] args) throws Exception {
new EchoServer(null, 8989);
}
}
So For this code, I have a few questions. One, if I read 10 bytes, but I don't want to do anything until I read 100 bytes, how would I implement that? Also, say I only want to write when a counter reaches a certain number, how would I implement that non-blocking? The thing about this code is, is that it will echo no matter how big the bytebuffer is. How do I change that so it would only echo when it has 100 bytes? How can I write only if a counter is a certain size? Thanks!
Would putting an if(numRead < 100) {do rest} else {return} in the read method work for the first problem?
Also, would putting an if(counter > 100) {do rest} else{return} in the write method work for the second?
You have to code that part, basically you need to keep track of bytes read, keep adding bytes read to temporary buffer and once you reached your required limit, you can pass that buffer to your worker thread.
I'll recommend you to use netty it provides all the things which you are looking for out of the box.
Look at this link.
Hope this helps
Did channel with non blocking mode return -1 while reading?? For your question, You can set the bytebuffer limit:-
For example:-
ByteBuffer buff = ByteBuffer.allocate(1024);
buff.clear();
buff.limit(your_limit);//is this what you want??
while(buff.remaining>0&&channel.read(buff)); // if will reach till your limit only.
System.out.println(new String(buff.array()));
hope this help

How to do Java serialization with ObjectInputStream when using NIO Selector?

How can you read an object directly from a SocketChannel that is non-blocking? It's being accessed with a Selector. The following code is broken (it throws an IllegalBlockingModeException) and I don't know how to fix it, except for perhaps using ByteBuffer, which I'd rather not (for now, at least):
public static void main(String[] args) {
try {
Selector selector = Selector.open();
ServerSocketChannel listener = ServerSocketChannel.open();
listener.socket().bind(new InetSocketAddress(50001));
listener.configureBlocking(false);
listener.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> i = selector.selectedKeys().iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
i.remove();
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
ObjectInputStream ois = new ObjectInputStream(channel.socket().getInputStream());
String message = (String) ois.readObject();
System.out.println("Server received message: " + message);
}
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}

Categories