NIO on sun doesnot give WRITE notification


S

Suresh

Hi,


I am trying to write a small program using the NIO class that would read
from a socket some data, process it and then send the result back. For this
I need to register the channel for OP_READ first, when I have processed the
data and have some results I would set it to OP_READ | OP_WRITE. When I dont
have anything more to write to the channel, I set it back to OP_READ.

This seems to work fine on a linux and Windows system. But it doesnt work on
the sun system. I get a WRITE notification only once in the begning. But
never again. Am I going something wrong here ? Is there a better way to do
this ?

I have attached the code I used. (The code is very curde one, but helps show
the problem.)

The system that I used for testing is:
$ uname -X
System = SunOS
Node = suntest
Release = 5.10
KernelID = Generic
Machine = i86pc
BusType = <unknown>
Serial = <unknown>
Users = <unknown>
OEM# = 0
Origin# = 1
NumCPU = 2

$ java -version
java version "1.5.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Java HotSpot(TM) Client VM (build 1.5.0_02-b09, mixed mode)


Thanks for your time.




<CODE>

import java.net.*;
import java.io.*;
import java.util.*;
import java.nio.channels.*;
import java.nio.*;
import java.nio.channels.spi.*;



public class SelectorTestServer {

/** Single selector for accepts, reads */
private Selector readSelector = null;

/** ServerSocketChannel which listens for client connections*/
private ServerSocketChannel ssch = null;

/** The thread that waits for ready Channels - accept / read*/
private SelectorThread readThread = null;

/** Input queue **/
private Vector inData=new Vector();

/** Output queue **/
private Vector outData=new Vector();

private ByteBuffer inBuff;

private ByteBuffer outBuff;

private Object readLock=new Object();

/** Thread which runs the Selector */
private class SelectorThread extends Thread {
public SelectorThread() {
super("SelectorThread");
}

public void run () {
boolean running = true;
try{
// block until a Channel is ready for I/O
while (1==1) {
System.out.println("Going to select:
"+readSelector.keys().size());
while(readSelector.select() > 0){
System.out.println("In buffer: "+inData.size()+" Out
buffer: "+outData.size());
System.out.println("Read Thread while loop");
Set readyKeys = readSelector.selectedKeys();
Iterator i = readyKeys.iterator();
while (i.hasNext()){
SelectionKey sk = (SelectionKey) i.next();
i.remove();
if (sk.isValid() && sk.isAcceptable()){
// new client connection
ServerSocketChannel nextReady = (ServerSocketChannel)
sk.channel();
SocketChannel channel = nextReady.accept();
channel.configureBlocking(false);
channel.register(readSelector, SelectionKey.OP_READ);
WriteThread writeThread=new
WriteThread(channel);
writeThread.start();
}
if (sk.isValid() && sk.isReadable()){
System.out.println("****** readable");
SocketChannel
channel=(SocketChannel)sk.channel();
inBuff=ByteBuffer.allocate(1024);
int len=channel.read(inBuff);

if (len == -1 || len == 0) {
// Socket closed
channel.close();
}else{
inData.add(inBuff);
System.out.println("Read "+len+"
bytes");
synchronized(readLock){
readLock.notify();
}
}
}
if (sk.isValid() && sk.isWritable()) {
System.out.println("****** writable");
SocketChannel
channel=(SocketChannel)sk.channel();
if (outData.size() > 0) {

outBuff=(ByteBuffer)outData.remove(0);
outBuff.flip();
int len=channel.write(outBuff);
System.out.println("Sent ... "+len);
}

if (outData.size() == 0) {
// Nothing to send anymore ... so,
only reading

channel.register(readSelector,SelectionKey.OP_READ);
}
}
}
}
}
}
catch (Exception ex){
System.out.println("Exception in selector loop: ");
ex.printStackTrace();
running = false;
}
System.out.println("Selector thread terminated");
System.exit(0);
} //end run()
} //end class

/** Thread which runs the Selector */
private class WriteThread extends Thread {
private SocketChannel channel;
public WriteThread(SocketChannel chnl) {
super("WriteThread");
channel=chnl;
}

public void run () {
boolean running = true;
try{
while (running) {
System.out.println("Write thread: In buffer:
"+inData.size()+" Out buffer: "+outData.size());
if (inData.size() > 0) {
System.out.println("Write thread ... ");
ByteBuffer tmp=(ByteBuffer)inData.remove(0);
outData.add(tmp);
System.out.print("[");
readSelector.wakeup();
System.out.print(".");

channel.register(readSelector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
System.out.println("]");
}

System.out.println("Write Thread: Waiting for data ...");
synchronized(readLock){
readLock.wait();
}
System.out.println("Write Thread: got data ...
"+inData.size());
}
}
catch (Exception ex){
System.out.println("Write Thread: Exception in write selector loop:
");
ex.printStackTrace();
running = false;
}
} //end run()
} //end class

/**
Sets up the selectors and starts listening
*/
protected void startListening () {
try{
// create the selector and the ServerSocket
readSelector = SelectorProvider.provider().openSelector();
ssch = ServerSocketChannel.open();
ssch.configureBlocking(false);
InetSocketAddress isa = new
InetSocketAddress(InetAddress.getLocalHost(), 2000);
ssch.socket().bind(isa);
ssch.register(readSelector, SelectionKey.OP_ACCEPT);
}
catch (Exception e){
System.out.println("Error starting listening");
e.printStackTrace();
}
this.readThread = new SelectorThread();
this.readThread.setDaemon(true);
this.readThread.start();
}

// Test the working

public static void main (String argv[]){
SelectorTestServer s = new SelectorTestServer();
s.startListening();
try{
Thread.currentThread().sleep(500000);
}
catch (Exception e){
System.err.println(e.toString());
}
}

}

</CODE>
 
Ad

Advertisements

E

Esmond Pitt

The reason for this is abstruse, OP_WRITE being edge-triggered on Unix
but level-triggered on Windows, but anyway you don't want to do this,
it's not necessary or desirable.

A socket is almost always writable unless the remote reader is stalled.
Therefore you should think of OP_WRITE as being almost always true and
not register for it. When you have output, just write it. *If and only
if* you get a short write you should then register for OP_WRITE and
contine the write when you receive the OP_WRITE state. Unless you get
another short write result you should then deregister for OP_WRITE.

In other words every write should be followed by a test that registers
for OP_WRITE if the write was short, otherwise *deregisters* OP_WRITE.
 
S

Suresh

Esmond Pitt said:
The reason for this is abstruse, OP_WRITE being edge-triggered on Unix
but level-triggered on Windows, but anyway you don't want to do this,
it's not necessary or desirable.

A socket is almost always writable unless the remote reader is stalled.
Therefore you should think of OP_WRITE as being almost always true and
not register for it. When you have output, just write it. *If and only
if* you get a short write you should then register for OP_WRITE and
contine the write when you receive the OP_WRITE state. Unless you get
another short write result you should then deregister for OP_WRITE.

In other words every write should be followed by a test that registers
for OP_WRITE if the write was short, otherwise *deregisters* OP_WRITE.


Thanks for your reply.

Unfortunately, I cant set OP_WRITE to always true, for 2 reasons:

* Its always a READ followed by a WRITE. The program receives some message
through the socket, which must be processed and then the result must be
written back to the socket.

* setting OP_WRITE to true always, results in lot of write notification,
which is not desired as it uses lot of CPU.

This program works on Linux and Windows system without problem.

It works on this sun system: (SunOS sun1 5.8 Generic_117350-04 sun4u sparc
SUNW,Sun-Fire-V210 Solaris)

but does not work on (SunOS suntest 5.10 Generic i86pc i386 i86pc)

Desired result:
* set the channel to OP_READ on connect.
* read from channel on read notification.
* process the data
* set the channel to OP_READ | OP_WRITE
* write the result to channel on write notification.
* when there is nothing to write set the channel to OP_READ.
* loop for more data.

Problem:
on (SunOS suntest 5.10 Generic i86pc i386 i86pc)
we never get write notification, but the selection keys are changed.
 
T

Thomas Schodt

Suresh said:
Unfortunately, I cant set OP_WRITE to always true

That was not what was suggested.
* setting OP_WRITE to true always, results in lot of write notification,
which is not desired
Exactly.

Desired result:
* set the channel to OP_READ on connect.
* read from channel on read notification.
* process the data

write the result

if the write is short register for OP_WRITE
* write [more of] the result to channel on write notification.
 
Ad

Advertisements

E

Esmond Pitt

Suresh said:
Thanks for your reply.

Unfortunately, I cant set OP_WRITE to always true, for 2 reasons:

That is *exactly* the opposite of what I recommended you to do.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top