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();
}
Related
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();
}
}
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.
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.
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?
This is an Echo Server. I cannot understand why after first connection with client, even after client close the connection still the server is printing "Reading.." and "Writing..". Shouldn't the server block with select() method?
Thanks
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
public class EchoServer
{
public static int DEFAULT_PORT=7;
public static void main(String [] args)
{
ServerSocketChannel serverChannel;
Selector selector;
try
{
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(DEFAULT_PORT);
ss.bind(address);
serverChannel.configureBlocking(false);
selector=Selector.open();
serverChannel.register(selector,SelectionKey.OP_ACCEPT);
} catch(IOException ex) {ex.printStackTrace(); return;}
while(true)
{
int selectednum=0;
try{
selectednum=selector.select(); //blocks
}catch (IOException ex) {ex.printStackTrace(); break;}
if (selectednum>0) {
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key=iterator.next();
iterator.remove();
try{
if (key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted from "+client);
client.configureBlocking(false);
SelectionKey clientKey=client.register(
selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(100);
clientKey.attach(buffer);
}
if (key.isReadable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
System.out.println("Reading..");
client.read(output);
}
if (key.isWritable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
System.out.println("Writing..");
client.write(output);
output.compact();
}
} catch (IOException ex) {
key.cancel();
try { key.channel().close();}
catch (IOException cex) {};
}
}
}
}
}
}
You aren't detecting end of stream when reading from the client. The read() method returns -1. When that happens you should close the channel.