infinited loop with java selector - java

I am a new comer in Java, now I got puzzled with java nio selector, below are the code from the book of java network program 3rd,
package org.eclipse.java.socket.samples;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.Iterator;
import java.util.Set;
public class ChargenServer {
public static int DEFAULT_PORT = 4321;
public static void main(String[] args) {
int port;
try {
port = Integer.parseInt(args[0]);
}
catch (Exception ex) {
port = DEFAULT_PORT;
}
System.out.println("Listening for connections on port " + port);
byte[] rotation = new byte[95 * 2];
for (byte i = ' '; i <= '~'; i++) {
rotation[i - ' '] = i;
rotation[i + 95 - ' '] = i;
}
ServerSocketChannel serverChannel;
Selector selector;
try {
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
catch (IOException ex) {
ex.printStackTrace();
return;
}
while (true) {
try {
selector.select();
}
catch (IOException ex) {
ex.printStackTrace();
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
SocketChannel client = server.accept();
System.out
.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey key2 = client.register(selector,
SelectionKey.
OP_WRITE);
ByteBuffer buffer = ByteBuffer.allocate(74);
buffer.put(rotation, 0, 72);
buffer.put((byte) '\r');
buffer.put((byte) '\n');
buffer.flip();
key2.attach(buffer);
}
else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
if (!buffer.hasRemaining()) {
// Refill the buffer with the next line
buffer.rewind();
// Get the old first character
int first = buffer.get();
// Get ready to change the data in the buffer
buffer.rewind();
// Find the new first characters position in
// rotation
int position = first - ' ' + 1;
// copy the data from rotation into the buffer
buffer.put(rotation, position, 72);
// Store a line break at the end of the buffer
buffer.put((byte) '\r');
buffer.put((byte) '\n');
// Prepare the buffer for writing
buffer.flip();
buffer.compact();
}
client.write(buffer);
}
}
catch (IOException ex) {
key.cancel();
try {
key.channel().close();
}
catch (IOException cex) {
}
}
}
}
}
}
The server is quite simple, get a connection, then echo a serial letters to the clients,
but when i run it on my Ubuntu10.10 with
Java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.4) (6b20-1.9.4-0ubuntu1)
OpenJDK Server VM (build 19.0-b09, mixed mode)
I got a infinite loop, I really do not know why, help me please!
Thanks everybody, but i still be confused with selector, now let's make things more easy to show my confused, see the code:
package org.eclipse.java.socket.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class SocketSelector {
public static void main(String[] args) throws IOException {
// Create selector
Selector selector = null;
selector = Selector.open();
////////////////////////////////////////////////////////////////////////
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(
"localhost", 4321));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT);
/*
* Let's begin select
*/
while (true) {
selector.select();
System.out.println("Hello, selector!");
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey )it.next();
if (key.isReadable()) {
System.out.println("It's readable!");
}
it.remove();
}
}
}
}
in my understand, "selector.select()" wait a input event from remote server, then it.remove() remove this event, so the selector begin to wait for new event from remote server, so the client can get data from server with selector in a continuous way, but the result is looped again and again, the selector make no sense to the server's data,
why?
Anything wrong with my code?

Three are multiple issues w/ the code, incl. not closing the selectors.
You need to register for OP_WRITE only if the write operation fails to write the entire Buffer, and unregister otherwise. Look at interestedOps().
Generally, you need OP_READ in order to read from that channel.
Finally ALWAYS check http://bugs.sun.com before stackoverflow (it's one of my tracked bugs). Advise: don't use the same selector to accept/write.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4919127
cheers

Once you have written everything you want (i.e., when the out-buffer is empty) you should remove the OP_WRITE interest flag.

Related

Selector.select() not being unblocked even after I register a channel ready to read from another thread

I am creating a server to handle many different connections at the same time. I create two Selectors, on for the serverSocketChannel to accept and then the other for the connections to read data.
The one selector successfully gets past the blocking select() function to accept the new connection. Then the goal is to register that SocketChannel that was accepted with the other selector that is currently blocked by it's select() function so that I can read the data when I need to.
Everything seems to be set up correctly, but even after sending data, I am not getting past the other selector's select() function.
Here is my server file:
import java.io.IOException;
import java.net.InetSocketAddress;
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.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class Server implements Runnable
{
public Thread threadAccept;
public Thread threadRead;
protected ServerSocketChannel serverSocket;
protected Selector selectorAccept;
protected Selector selectorIO;
protected Set<Connection> connections;
private ByteBuffer buffer;
public Server()
{
try
{
selectorAccept = Selector.open();
selectorIO = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.configureBlocking(false);
InetSocketAddress hostAddress = new InetSocketAddress("localhost",4444);
serverSocket.bind(hostAddress);
int ops = serverSocket.validOps();
SelectionKey selectKey = serverSocket.register(selectorAccept, ops);
threadAccept = new Thread(this);
threadAccept.start();
threadRead = new Thread(this);
threadRead.start();
}
catch(Exception e)
{
System.out.println("Error "+e);
}
}
public void run()
{
if(Thread.currentThread() == threadAccept)
{
acceptNewConnections();
}
else if(Thread.currentThread() == threadRead)
{
readData();
}
}
private void acceptNewConnections()
{
int numberOfKeys = 0;
while(true)
{
try
{
numberOfKeys = selectorAccept.select();
Set<SelectionKey> keys = selectorAccept.selectedKeys();
Iterator<SelectionKey> itr = keys.iterator();
while(itr.hasNext())
{
SelectionKey key = itr.next();
if(key.isAcceptable())
{
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selectorIO, SelectionKey.OP_READ);
System.out.println("New connection");
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
public void readData()
{
int numberOfKeys = 0;
ByteBuffer buffer = ByteBuffer.allocate(256);
while(true)
{
try
{
System.out.println("About to block on IO selector");
numberOfKeys = selectorIO.select();
System.out.println("I NEVER GET HERE");
Set<SelectionKey> keys = selectorIO.selectedKeys();
Iterator<SelectionKey> itr = keys.iterator();
while(itr.hasNext())
{
SelectionKey key = itr.next();
if(key.isReadable())
{
SocketChannel channel = (SocketChannel)key.channel();
channel.read(buffer);
String s = buffer.toString();
System.out.println(s);
}
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
}
And here is my main class to kick the server off and also create a client.
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class Main
{
public static void main(String[] args)
{
Server s = new Server();
Thread t = new Thread(new Runnable() {
public void run()
{
try
{
Socket s = new Socket("localhost", 4444);
byte[] data = "hello".getBytes();
s.getOutputStream().write(data);
s.getOutputStream().flush();
} catch (UnknownHostException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t.start();
}
}
My guess is that a wakeup would be sufficient
client.register(selectorIO, SelectionKey.OP_READ);
selectorIO.wakeup();
Or just execute a task on the client thread where it registers the client on the same selectorIO it is waiting for. So the client thread should also check for a task queue as part of its loop (e.g. ConcurrentLinkedQueue) and call a selectorIO.wakup after placing the task in the task queue.

java nio socket not detecting when machine goes to sleep or hibernates

Here are simplified versions of my socket server and client components.
The primary goal is for the client to detect when the server goes down and for the server to detect when the client goes down.
This works perfectly (on Windows) when either the client or the server are killed (getting IOException "An existing connection was forcibly closed by the remote host").
I would also like to detect when the machine where the client or server is running goes to sleep (or hibernates), eventually using the same mechanism.
Instead, the current behavior is that "the other machine going to sleep" event is not detected, and when the machine is woken up the connection is live again. At this time "the process going down" event is detected as before.
In the case where the client machine goes to sleep, the culprit seems to be "selector.selectedKeys()" not returning a key for the connection to the sleeping machine.
Is this functionality missing in the socket implementation on Windows?
Does anybody have any suggestion on how to fix / go around this issue?
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
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.Iterator;
import java.util.Set;
public class TestServer {
private ByteBuffer _inBuf;
private int _serverPort;
public static void main(String[] args) {
TestServer server = new TestServer(7071);
server.start();
}
public TestServer(int serverPort) {
_serverPort = serverPort;
}
public void start() {
_inBuf = ByteBuffer.allocate(512);
System.out.println("Server starting on port "+_serverPort);
new Thread() {
public void run() {
try {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(_serverPort));
server.configureBlocking(false);
SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {
SelectionKey key = i.next();
i.remove();
if (key == serverKey) {
if (key.isAcceptable()) {
System.out.println("acceptable server key "+Integer.toHexString(key.hashCode()));
try {
SocketChannel client = server.accept();
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
System.out.println("registered client key "+Integer.toHexString(clientKey.hashCode()));
} catch (IOException x) {
x.printStackTrace();
}
}
} else {
if (!key.isReadable()) continue;
SocketChannel client = (SocketChannel) key.channel();
System.out.println("reading "+Integer.toHexString(key.hashCode()));
try {
int no = client.read(_inBuf);
if (no<0) throw new IOException("reached end-of-stream"+Integer.toHexString(key.hashCode()));
if (no>0) System.out.println("read "+no+" bytes from "+Integer.toHexString(key.hashCode()));
} catch (IOException x) {
System.out.println(x.getMessage()+" "+Integer.toHexString(key.hashCode()));
key.cancel();
try {
client.close();
} catch (IOException ignore) {
ignore.printStackTrace();
}
continue;
}
_inBuf.flip();
_inBuf.compact();
}
}
}
} catch (Exception x) {
x.printStackTrace();
}
}
}.start();
}
}
and
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class TestClient {
private static final int _connectionTimeoutNanos = 10 * 1000000;
private String _serverHost;
private int _serverPort;
private SocketChannel _channel = null;
private ByteBuffer _inBuf;
public static void main(String[] args) {
TestClient client = new TestClient("192.168.1.180", 7071);
client.start();
}
public TestClient(String serverHost, int serverPort) {
_serverHost = serverHost;
_serverPort = serverPort;
}
public void start() {
_inBuf = ByteBuffer.allocate(512);
ClientThread thread = new ClientThread();
thread.start();
}
private class ClientThread extends Thread {
#Override
public void run() {
System.out.println("Client connecting to "+_serverHost+":"+_serverPort);
SocketAddress socketAddress = new InetSocketAddress(_serverHost, _serverPort);
while (true) {
boolean connected = false;
try {
_channel = SocketChannel.open();
_channel.configureBlocking(false);
try {
connected = _channel.connect(socketAddress);
} catch (IOException x) {
try {
_channel.close();
} catch (Throwable suppressed) {
x.addSuppressed(suppressed);
}
throw x;
}
long nanoStart = System.nanoTime();
while (!connected) {
connected = _channel.finishConnect();
if (!connected && (nanoStart+_connectionTimeoutNanos < System.nanoTime())) {
throw new IOException("Non blocking connect failed");
}
}
_channel.socket().setSoLinger(true, 10);
System.out.println("Connected to "+_serverHost+":"+_serverPort);
while (true) {
if (!readFromChannel()) break;
}
System.out.println("Disconnected from "+_serverHost+":"+_serverPort);
} catch (IOException x) {
if (connected) {
System.out.println("Disconnected from "+_serverHost+":"+_serverPort+" "+x.getMessage());
}
}
try {Thread.sleep(1000);} catch (InterruptedException x) {}
}
}
}
public boolean readFromChannel() throws IOException {
int no = _channel.read(_inBuf);
if (no<0) {
return false;
}
if (no>0) System.out.println("read "+no+" bytes from "+_serverHost+":"+_serverPort);
_inBuf.flip();
_inBuf.compact();
return true;
}
}
This behavior differs from system to system and even its configuration. Old versions of Windows used to shut down all the pending connections when computer became sleeping and even when temporarily lost network connectivity. This is often not what the user wanted, because in case of just temporary outages the user had to reopen all the connections again. So it had changed some time ago and now (by default, it's configurable) it behaves similarly to other systems (Linux, MacOs, ...). So the connection is kept until it timeouts.
To avoid long living dead connections the best option is to set SO_KEEPALIVE option on the socket. Both sides and their operating systems will then send dummy packets over the socket (not payload data so not visible to application layer) and unless receiving response in reasonable time, OS will kill the connection. In Java you can achieve this like following:
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
Thanks Zbynek, that solved the issue :-).
Here is what I had to do:
1) in the TestServer code, after client.configureBlocking(false) at line 50 I added:
client.socket().setKeepAlive(true);
which is equivalent to your
client.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
2) in the TestClient code, after line 60:
_channel.socket().setSoLinger(true, 10);
I added:
_channel.socket().setKeepAlive(true);
3) using regedit, on both Windows machines, I added the following value under
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters/
KeepAliveTime REG_DWORD 1000
Its default value is 2 hours and I reduced it to 1 second.
I left the KeepAliveInterval at its default value of 1 second and TcpMaxDataRetransmissions at its default value of 5.
Like with any Microsoft software, I had to restart both machines.
Note that one of my machines is Win10 and the other is Win7.
With these changes, whichever machine goes to sleep, the component on the other machine detects the disconnect event (within 5 seconds). As soon as the machine wakes up, the component on it detects that the connection is no longer there and then re-connects fresh. Exactly what I was trying to accomplish.
Thanks again,
Vladimir

NIO SocketChannel saying there is no data when there is (or selector is not informing me)

I have a functioning client-server apparatus which can successfully connect and send messages to each other using NIO.
Right now my only confusion is how I'm supposed to continue reading when socketChannel.read() returns zero.
I have a protocol that sends the first 4 bytes as the number of incoming bytes to expect. Even with that amount, I'm running into a potential issue.
For testing, However, there are times where I might read something like:
5 // Read 5 bytes when calling socketChannel.read()
0 // Read 0 bytes when calling socketChannel.read() immediately after
When I hit the zero, I assumed that I was done reading and need to wait for more data to come to me.
When I do this however, OP_READ doesn't seem to be triggered when I perform selectNow() again later on. I checked the key and it has it's readyops() and interestops() set to 1 (which is OP_READ), but it does not want to recognize that it's time to read again.
I found that if I continue looping to read, I might get something like:
5 // socketChannel.read()
0 // socketChannel.read()
7 // socketChannel.read() (Done since I have all my bytes)
0
0
0
...
I'm confused here because this means one of:
There is no data there, so the 0 available is legitimate, but then when the rest of the data comes in, the selector refuses to return the key with selectNow()
The data is all there, but for some reason returns 0 on reading.
Am I supposed to re-register the channel after a selectNow() returns it as an active key? (Though I didn't have to between switching from OP_CONNECT to OP_READ... so I'm guessing not). I feel like blindly circling in a loop is dangerous and will waste processing cycles.
Am I just supposed to keep polling them? That makes me confused at when OP_READ actually fires then.
This was due to an error on my part, where I did not call .clear() on the bytebuffer that reads. This causes it to return 0 read even though the data has streamed in.
This example may also be of use to people who want to see how a simple client works (though with really bad exception handling). There is no guarantee this will work properly and may likely have issues since it was designed to be a quick and dirty test.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class Test {
public static final int PORT = 22222;
public static void main(String[] args) throws IOException {
Thread s = new Thread(new Server());
Thread c = new Thread(new Client());
s.start();
c.start();
}
}
class Client implements Runnable {
public Selector selector;
public SocketChannel sc;
public Client() throws IOException {
selector = Selector.open();
}
#Override
public void run() {
try {
sc = SocketChannel.open();
sc.socket().setTcpNoDelay(true);
sc.configureBlocking(false);
SelectionKey k = sc.register(selector, SelectionKey.OP_CONNECT);
boolean firstConnect = sc.connect(new InetSocketAddress("localhost", Test.PORT));
if (firstConnect) {
System.out.println("Connected on first connect, de-registering OP_CONNECT");
k.interestOps(SelectionKey.OP_READ);
}
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isConnectable()) {
boolean finishConnectResult = sc.finishConnect();
key.interestOps(SelectionKey.OP_READ);
System.out.println("Finished connection: " + finishConnectResult);
}
if (key.isReadable()) {
ByteBuffer bb = ByteBuffer.allocate(2);
int bytesRead = 0;
while ((bytesRead = sc.read(bb)) > 0) {
bb.flip();
System.out.println(bytesRead + " bytes read");
System.out.println(bb.get() + ", " + bb.get());
//bb.clear(); // If this is not commented, it will not be handled properly.
}
System.out.println("Last bytes read value = " + bytesRead);
System.exit(0);
}
}
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
class Server implements Runnable {
public Selector selector;
public SocketChannel sc;
public Server() throws IOException {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(Test.PORT));
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
#Override
public void run() {
boolean notSentData = true;
try {
while (true) {
int keys = selector.selectNow();
if (keys > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
sc = ssc.accept();
if (sc != null) {
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true); // Required in my application
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println("Server accepted connection");
} else {
System.out.println("Got null connection");
}
}
}
}
if (sc != null && notSentData) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{ 1, 2, 3, -1});
bb.flip();
int wrote = sc.write(bb);
System.out.println("Wrote " + wrote + " bytes");
notSentData = false;
}
Thread.sleep(5);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}

NIO Thread CPU usage

I am running into CPU usage problem when I am using a
java.nio.channel.Selector.
when the server thread started, it initially consume 200% cpu resource and dramatically drop down to 0.1%. but if it is connected by a client. this number rapidly increases to 97% - 100% and keep that number even after the client disconnected.
here is the code I wrote for server.
package com.cs.gang.test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
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.Iterator;
public final class TCPConnectionServer implements Runnable {
private RandomAccessFile logFile;
private Selector selector;
public TCPConnectionServer() throws IOException {
final File logFile = new File("server_log.txt");
if (logFile.exists()) {
logFile.delete();
}
this.logFile = new RandomAccessFile(logFile.getCanonicalPath(), "rw");
System.out.println(logFile.getCanonicalPath());
selector = Selector.open();
final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
#Override
public void run() {
while (true) {
try {
if (selector.select() > 0) {
final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
final SelectionKey key = keys.next();
keys.remove();
if (key.channel() instanceof SocketChannel) {
if (!((SocketChannel) key.channel()).isConnected()) {
logFile.writeChars(((SocketChannel) key.channel()).toString() + " is off line");
}
}
if (key.isAcceptable()) {
final ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
final SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
logFile.writeChars(clientChannel.toString() + "is now connected");
} else if (key.isReadable()) {
final SocketChannel client = (SocketChannel) key.channel();
if (client.isConnected()) {
final ByteBuffer buffer = ByteBuffer.allocate(1024);
int byteRead = -1;
final StringBuilder sb = new StringBuilder(client.toString()).append(" : ");
while ((byteRead = client.read(buffer)) > 0) {
sb.append(new String(buffer.array()), 0, byteRead);
buffer.clear();
}
logFile.writeChars(sb.toString());
System.out.println(sb.toString());
} else {
System.out.println("Closed Connection detected");
}
}
}
} else {
System.out.println("Sleep for 100ms");
Thread.sleep(100);
}
} catch (final IOException e) {
e.printStackTrace();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new Thread(new TCPConnectionServer()).start();
}
}
can any one help me out? I am new to NIO and I am having absolutely no idea about this problem now.
Thanks
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
The problem is here. OP_WRITE is almost always ready, so your selector will rarely block and usually spin. This is a misuse of OP_WRITE. The correct way to use it is as follows:
Write whenever you have something to write.
If write() returns zero, register the socket for OP_WRITE and return to the selection loop. Of course you'll also have to save the ByteBuffer you were writing from, in association with the channel: this is normally done via the SelectionKey's attachment, either directly or indirectly. Ideally you will have both a read and a write ByteBuffer per channel, saved in a channel context object which in turn is saved as the key attachment.
When OP_WRITE fires, continue writing from that ByteBuffer. If this completes, i.e. write() does't return zero or a short write count, de-register the channel for OP_WRITE.

NIO Hang Up Problems?

I'm having problems with sending data over SocketChannels between a Host and Client using the NIO framework.
I've never really bothered to learn NIO before now, but with the introduction of the java.nio.files package and other various improvements I thought I would give it a try.
I'm able to get the SocketChannel and ServerSocketChannel to connect fine, but the actual data transfer is acting very weird. It NEVER finishes correctly on the Client side, always hanging up after the final read. Furthermore, it sometimes reads the incorrect amount of data (too much or too little) and has even caused Windows Explorer to go crazy and allocate literally ALL of the system's memory, crashing the computer.
Here is the code (it is test code) I have right now:
package bg.jdk7.io;
import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class NetIO {
static volatile long filesize;
public static void main(String[] args) {
new Thread(new Client()).start();
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(5555));
SocketChannel sc = ssc.accept();
if(sc.isConnected()) {
ByteBuffer buff = ByteBuffer.allocate(10240);
Path fp = Paths.get(System.getProperty("user.home")+"\\Documents\\clip0025.avi");
if(Files.exists(fp)) {
FileChannel fc = (FileChannel) Files.newByteChannel(fp, StandardOpenOption.READ);
long tot = Files.size(fp);
long run = 0;
int read = 0;
int prog = 0;
while((read = fc.read(buff))>0) {
buff.rewind();
sc.write(buff);
run+=buff.position();
int last = prog;
prog = (int)(((double)run/tot)*100);
if(prog !=last) {
System.out.println(prog + "%");
}
buff.flip();
}
fc.close();
System.out.println("Sending completed");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class Client implements Runnable {
public void run() {
try {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost",5555));
if(sc.isConnected()) {
Path dpf = Paths.get("\\NIO_TESTING\\");
Path dp = Paths.get(dpf+"\\clip.avi");
Files.createDirectories(dpf);
FileChannel fc = (FileChannel) Files.newByteChannel(dp, CREATE, WRITE, TRUNCATE_EXISTING);
ByteBuffer buff = ByteBuffer.allocate(10240);
int read;
int total = 0;
while((read = sc.read(buff))>0) {
total+=read;
buff.rewind();
fc.write(buff);
System.out.println(fc.size());
buff.flip();
if(total == filesize) System.out.println("File data received successfully...");
}
System.out.println("Completed successfully");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
A SocketChannel resulting from ServerSocketChannel.accept() is connected. There is no way it can't be. The isConnected() test is pointless.
Your server I/O code is incorrect. Channel writes must be preceded by buffer.flip() and followed by buffer.compact(). The canonical way to copy from one channel to another is as follows (note that this behaves correctly at EOS even when there is pending data still in the buffer):
while (in.read(buffer) >= 0 || buffer.position() > 0)
{
buffer.flip();
out.write(buffer);
buffer.compact();
}
Similarly to my first paragraph, a SocketChannel resulting from SocketChannel.open() followed by SocketChannel.connect() is connected: again, the test is pointless. If it wasn't connected there would have been a ConnectException on the connect() call.
Your client I/O code has the same problems as your server I/O code.
You aren't closing the SocketChannel in the server, so the client will never stop reading from the connection.
You aren't closing the SocketChannel in the client either.
The File.exists() test is pointless. The following line will throw an exception if the file isn't there, and you have to handle that exception anyway, so why do it all again?

Categories