Not able to get the output in NIO - java

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?

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();
}
}

The simplest NIO-server example

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.

DatagramChannel not receiving any bytes on Android

Update
I tried the same implementation, but this time with ServerSocketChannel/SocketChannel. This works on Android. It seems that I am somehow having 100% packet loss over UDP on Android. Does anyone have thoughts on the cause for this? (It is not a WiFi-issue. The UDP client works from my notebook.)
Original
I am having trouble receiving packets in an Android application. The Android application is able to send data to the desktop server, who replies immediately, but always receives zero bytes. The same code works when I use it as a desktop client application.
My Android application has the INTERNET permission. I also tried it with NETWORK, CHANGE_WIFI_MULTICAST_STATE, ACCESS_WIFI_STATE and ACCESS_NETWORK_STATE. That made no difference, sadly.
Tried it both on a device and in an emulator.
Below is the code and a sample of the output.
MainActivity:
private Thread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread = new Thread(new Client());
thread.start();
}
Client:
public class Client implements Runnable {
private static final String LOG_TAG = "NET";
private static final String IP = "--.---.---.--";
private static final int PORT = 6543;
private final ByteBuffer byteBuffer = ByteBuffer.allocate(256);
private boolean running;
public Client() {
running = false;
}
#Override
public void run() {
try {
final Selector selector = Selector.open();
final DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
datagramChannel.register(selector, SelectionKey.OP_WRITE);
datagramChannel.connect(new InetSocketAddress(IP, PORT));
running = true;
while (running) {
selector.select();
final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
final SelectionKey key = keys.next();
if (key.isReadable()) {
handleRead(key);
}
if (key.isValid() && key.isWritable()) {
handleWrite(key);
}
keys.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleRead(final SelectionKey key) throws IOException {
final DatagramChannel channel = (DatagramChannel) key.channel();
byteBuffer.clear();
final SocketAddress from = channel.receive(byteBuffer);
byteBuffer.flip();
Log.i(LOG_TAG, String.format("Received %d bytes from %s", byteBuffer.limit(), from));
key.interestOps(SelectionKey.OP_WRITE);
}
private void handleWrite(final SelectionKey key) throws IOException {
final DatagramChannel channel = (DatagramChannel) key.channel();
byteBuffer.clear();
byteBuffer.putInt(1234);
byteBuffer.flip();
final SocketAddress to = new InetSocketAddress(IP, PORT);
final int bytes = channel.send(byteBuffer, to);
Log.i(LOG_TAG, String.format("Send %d bytes to %s", bytes, to));
key.interestOps(SelectionKey.OP_READ);
}
}
Server:
public class Server implements Runnable {
private static final int PORT = 6543;
private final ByteBuffer byteBuffer = ByteBuffer.allocate(256);
private SocketAddress from;
private boolean running;
public Server() {
from = null;
running = false;
}
#Override
public void run() {
try {
final Selector selector = Selector.open();
final DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
datagramChannel.socket().setReuseAddress(true);
datagramChannel.register(selector, SelectionKey.OP_READ);
datagramChannel.bind(new InetSocketAddress(PORT));
running = true;
while (running) {
selector.selectNow();
final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
final SelectionKey key = keys.next();
if (key.isReadable()) {
handleRead(key);
}
if (key.isValid() && key.isWritable()) {
handleWrite(key);
}
keys.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleRead(final SelectionKey key) throws IOException {
final DatagramChannel channel = (DatagramChannel) key.channel();
byteBuffer.clear();
from = channel.receive(byteBuffer);
byteBuffer.flip();
System.out.println(String.format("Received %d bytes from %s", byteBuffer.limit(), from));
key.interestOps(SelectionKey.OP_WRITE);
}
private void handleWrite(final SelectionKey key) throws IOException {
final DatagramChannel channel = (DatagramChannel) key.channel();
if (from != null) {
byteBuffer.clear();
byteBuffer.putInt(1234);
byteBuffer.flip();
final int bytes = channel.send(byteBuffer, from);
System.out.println(String.format("Send %d bytes to %s", bytes, from));
}
key.interestOps(SelectionKey.OP_READ);
}
public static void main(String args[]) {
new Thread(new Server()).start();
}
}
Client output:
Send 4 bytes to /--.---.---.--:6543
Received 0 bytes from null
Send 4 bytes to /--.---.---.--:6543
Received 0 bytes from null
Server output:
Received 4 bytes from /--.---.---.--:52974
Send 4 bytes to /--.---.---.--:52974
Received 4 bytes from /--.---.---.--:52974
Send 4 bytes to /--.---.---.--:52974

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