Simply said, I'm able to receive data from the connected socket, until about 10 seconds after it starts. The solution to fix this is, to send data to the 'client'(an ARDrone) to keep the data stream going, otherwise it will stop sending data to the phone. However, my current code for some reason only writes data to the client (the ARDrone) on first connect and never after. I need it to keep sending data to the ARDrone even after the socket is communicating.
I've tried moving around the channel.register() calls, but nothing I've done seems to send data to the ARDrone, as I need.
Creating the connection:
channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(video_port));
channel.connect(new InetSocketAddress(drone_addr, video_port));
selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
Here is the current send/receive data function.
public void run() {
try {
ByteBuffer inbuf = ByteBuffer.allocate(BUFSIZE);
done = false;
while (!done) {
selector.select();
if (done) {
disconnect();
break;
}
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isWritable()) {
byte[] trigger_bytes = { 0x01, 0x00, 0x00, 0x00 };
ByteBuffer trigger_buf = ByteBuffer.allocate(trigger_bytes.length);
trigger_buf.put(trigger_bytes);
trigger_buf.flip();
channel.write(trigger_buf);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
inbuf.clear();
int len = channel.read(inbuf);
if (len > 0) {
inbuf.flip();
final BufferedVideoImage vi = new BufferedVideoImage();;
vi.addImageStream(inbuf);
drone.videoFrameReceived(0, 0, vi.getWidth(), vi.getHeight(), vi.getJavaPixelData(), 0, vi.getWidth());
}
}
}
} catch (Exception e) {
drone.changeToErrorState(e);
}
}
I believe you corrupt your event interest on the first write with this line:
channel.register(selector, SelectionKey.OP_READ);
Per documentation, this resets it to OP_READ only.
Edit 0:
Per your comment - yes, I think you should remove that line entirely, and do not treat read and write cases as alternatives. In general a socket could be readable and writable at the same time. So what's happening now is that the write case prevents the read case since UDP socket is always writable (versus TCP that buffers outgoing data).
So my suggestion is to not include OP_WRITE into that event set at all, but handle writing in some other fashion, say on a timer, or after each read, or whatever makes sense to your app.
Hope this helps.
Related
I'm using a function to read bytes from non-blocking SocketChannel (socket from accept()) and from blocking SocketChannel (client side). I'm implementing a server using selector to handle multiple clients, and I'm using loopback address to use my laptop only. I wrote this
while((r = socketChannel.read(ackBuf)) != -1) {
System.out.println(name3d+" r: "+r);
}
and I expected that when the end of the content in the channel was reached, read() would returned -1 but is not what succedes.
read(), in non-blocking configuration, return 0 also if nothing is ready to read at the moment but it will be soon (if I understand well) so if I change the code to
while((r = socketChannel.read(ackBuf)) > 0) {
System.out.println(name3d+" r: "+r);
}
I will not read nothing also if something will be ready a moment later.
How can I distinguish if I got 0 because is not ready or because it is ended?
In the following snippet I can test for a second time the read after a sleep but I'm sure is not the reliable way to do what I want.
int times = 0;
while((r = socketChannel.read(ackBuf)) != -1 && times<2) {
if (r == 0)
try {
Thread.sleep(500);
times++;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name3d+" r: "+r);
}
" if I got 0 because is not ready or because it is ended?" Do you mean the message or the totality of the communication?
For the message, you should use a communication protocol (like json or http) for the communication, I think you should get a SocketException... You would if you using blocking and the person on the other end closed the connection... (I've written to a lot of people on SO about how SocketException is your friend)
--- edit ---
Looking over the documention for Channel, it looks like you should get an IOException of some kind (SocketException is a subclass of IOException) if/when the channcel is closed
The Non-blocking SocketChannel is used a bit different.
You first wait for the selection key to tell you that there is data, and
then you read that data from the channel.
See this code draft:
Selector selector = Selector.open();
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(addr);
sc.register(selector, SelectionKey.OP_READ);
while (true) {
// select() can block!
if (selector.select() == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(1024);
sc.read(bb);
System.out.println("Message received!");
}
}
I've pasted a server side code snippet below. This server code works under normal circumstances, however, the following scenario manages to break the code.
Server and client are on the same machine. I used the loopback address, and the actual IP address, it makes no difference.
Scenario
Server is online, Client makes request (WritableByteChannel.write(ByteBuffer src) returns 12 byte, which is the correct size, but as research revealed that only means the 12 bytes are written to the TCP buffer).
Server program is turned off. Client notices that the channel is closed on the remote side and closes it on its own side, it doesn't make any requests.
Server is online again.
Client tries to make a request, but fails, because the channel is closed/invalid and can't be reused (even though server is online again).
Client checks server's online status, gets positive result, connects again and immediately makes another request.
Server accepts client (code below), after that processes the if clause with the key.isReadable() condition, but then fails on the read, which indicates end-of-stream.
It would be too complex to create an SSCCE, please comment if important information is missing or this is too abstract and I'll provide further information.
Question
How can a freshly created/accepted channel fail on the read operation?
What am I missing? What steps can I undertake to prevent this?
I already tried wireshark, but I can't capture any packets on the designated TCP port, even if the communication is acutally working.
Problem/Additional Info
It's possible to capture packets into .pcap file with RawCap
The problem was the way the client checked the server status. I've added the method below.
Code snippets
Snippet 1
while (online)
{
if (selector.select(5000) == 0)
continue;
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext())
{
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable())
{
log.log(Level.INFO, "Starting ACCEPT!");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
log.log(Level.INFO, "{0} connected to port {1}!",
new Object[] {channel.socket().getInetAddress().getHostAddress(), isa.getPort()});
}
boolean accepted = false;
if (key.isReadable())
{
log.log(Level.INFO, "Starting READ!");
SocketChannel channel = (SocketChannel) key.channel();
bb.clear();
bb.limit(Header.LENGTH);
try
{
NioUtil.read(channel, bb); // server fails here!
}
catch (IOException e)
{
channel.close();
throw e;
}
bb.flip();
Snippet 2
public static ByteBuffer read(ReadableByteChannel channel, ByteBuffer bb) throws IOException
{
while (bb.remaining() > 0)
{
int read = 0;
try
{
read = channel.read(bb);
}
catch (IOException e)
{
log.log(Level.WARNING, "Error during blocking read!", e);
throw e;
}
// this causes the problem... or indicates it
if (read == -1)
{
log.log(Level.WARNING, "Error during blocking read! Reached end of stream!");
throw new ClosedChannelException();
}
}
return bb;
}
Snippet 3
#Override
public boolean isServerOnline()
{
String host = address.getProperty(PropertyKeys.SOCKET_SERVER_HOST);
int port = Integer.parseInt(address.getProperty(PropertyKeys.SOCKET_SERVER_PORT));
boolean _online = true;
try
{
InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(host), port);
SocketChannel _channel = SocketChannel.open();
_channel.connect(addr);
_channel.close();
}
catch (Exception e)
{
_online = false;
}
return _online;
}
Solution
The problem was not the method that checked, if the service is available/the server is online. The problem was the second point EJP mentioned.
Specific input was expected from the server and it was left in an inconsistent state if that conditions were not met.
I've added some fallback measures, now the the reconnect process - including the check method - is working fine.
Clearly the client must have closed the connection. That's the only way read() returns -1.
Notes:
You're throwing the inappropriate ClosedChannelException when read() returns -1. That exception is thrown by NIO when you've already closed the channel and continue to use it. It has nothing to do with end of stream, and shouldn't be used for that. If you must throw something, throw EOFException.
You also shouldn't loop the way you are. You should only loop while read() is returning a positive number. At present you are starving the select loop while trying to read data that may never arrive.
I am learning Java nio selector. In my understanding, I thought the steps using selector is to firstly I register the my interest operations and then I can check the ready set and finally I can do the operations corresponding my interest operations. I don't know why in this code the writing process can happen in the block of if (key.isReadable()){...} but not if (key.isWritable){...} and why writing operation is not registered?
Iterator keys = sel.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey)keys.next();
if (!key.isValid())
continue;
if (key.isAcceptable()) {
// increase the counter
connection++;
// remove accept request
keys.remove();
// ACCEPT: get the server channel
ServerSocketChannel ssc =
(ServerSocketChannel) key.channel();
// init a socket for a client
SocketChannel nsc = ssc.accept();
nsc.configureBlocking(false);
// register the socket for READ
nsc.register(sel, SelectionKey.OP_READ);
}
}
while (count < COUNT_MAX + NUM_CHILD - 1) {
sel.select();
// Get all pending events and iterate
Iterator keys = sel.selectedKeys().iterator();
while ( keys.hasNext() ) {
SelectionKey key = (SelectionKey)keys.next();
keys.remove();
if (!key.isValid())
continue;
if (key.isReadable()) {
// READ: get the channel
SocketChannel nsc = (SocketChannel) key.channel();
// clear buffer for reading
readBuffer.clear();
int nBytes = nsc.read(readBuffer);
if (nBytes == -1) {// Check if the client closed the socket
key.channel().close();
key.cancel();
continue;
}
// Read a message
DataInputStream ist = new DataInputStream (
new ByteArrayInputStream(readBuffer.array()));
String msg = ist.readUTF();
System.out.print(msg + "\n");
// Clear the write buffer
writeBuffer.clear();
// Write the counter value on the buffer
count++;
if (count < COUNT_MAX)
writeBuffer.putInt(count);
else
writeBuffer.putInt(-1);
// flip the buffer and write on the channel
writeBuffer.flip();
// Reply to a client
nsc.write(writeBuffer);
}
} // while (key)
You don't need to register interest in OP_WRITE because usually the channel is ready for writing. However a WritableChannel, if in non blocking mode, might not succeed in writing all content of the given ByteBuffer. See here in its java docs:
Some types of channels,
depending upon their state, may write only some of the bytes or
possibly none at all. A socket channel in non-blocking mode, for
example, cannot write any more bytes than are free in the socket's
output buffer.
In this case you need to register the interest for OP_WRITE on the selector to be notified when the channel is once again ready for writing, so you can finish writing your ByteBuffer.
See here a related SO question.
I decided to make my UDPclient and UDPserver with java nio.
But I don't understand several things.
Here is the code
try {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.connect(remote);
//monitoring
Selector selector = Selector.open();
//read write keys
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024*64);//number of bytes for channel
while (true) {
selector.select(60000);//number of channels I think
Set readyKeys = selector.selectedKeys();
if (readyKeys.isEmpty()) {
break;
}
else {
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable( )) {
//read from buffer
channel.read(buffer);
}
if (key.isWritable()) {
//write to buffer
channel.write(buffer);
}
}
}
}
}
catch (IOException ex) {
System.err.println(ex);
}
If I write something in console the event in key.isWritable will occur? And if server sends something event isReadable will occur?
And I don't understand how to work with my events when for example user write "GETL" or "REGR"(my own methods).
The value you pass to select is a timeout not the number of channels.
You need to do
DatagramChannel channelFromKey = (DatagramChannel) key.channel();
not use channel
I don't understand what you mean by your own events. Read the Datagrams off the channel when that key is selected.
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable( )) {
DatagramChannel channelFromKey =
(DatagramChannel) key.channel();
buffer.clear();
// This is a DatagramChannel receive a datagram as a whole
channelFromKey.receive(buffer);
}
If I write something in console the event in key.isWritable will
occur?
No. The only events that will occur are on the channels you have registered with the selector. You haven't registered any channel to do with the console, and you can't, because only network channels are SelectableChannels, so you have to reason to expect events originating from the console to turn up via the Selector.
And if server sends something event isReadable will occur?
Yes.
And I don't understand how to work with my events when for example user
write "GETL" or "REGR"(my own methods).
Nor do I. I don't even understand the question. The only events you will get from the selector are the ones that it defines, on the channels you have registered with it.
I need to question about 11M nameservers and to find out which of them alive. In Java I use async sockets to send udp-requests and everything ok until I try to use multiple threads. The speed rises proportionally, but positive responses decrease dramatically, although I use a high-perfomance 16-core cluster.
I create a separate channel per thread and see no obvious reason why this happening. Can anyone explain what I'm doing wrong and is it okay to use different async sockets in threads?
Here's some code. So I have a lot of threads with id and it's list of hosts, each of them do the following:
#Override
public void run() {
DatagramChannel channel = null;
try {
channel = DatagramChannel.open();
InetSocketAddress isa = new InetSocketAddress(Settings.LOCAL_PORT+id);
channel.socket().bind(isa);
channel.configureBlocking(false);
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer outBuffer = ByteBuffer.wrap(Settings.QUERY);
ByteBuffer inBuffer = ByteBuffer.allocate(200);
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isReadable()) {
inBuffer.clear();
channel.receive(inBuffer);
inBuffer.flip();
inCounter++;
//some analize of response
continue;
}
if (key.isWritable()) {
if (outCounter < hosts.size()) {
channel.send(outBuffer, new InetSocketAddress(hosts.get(outCounter), Settings.DNS_PORT));
outBuffer.flip();
outCounter++;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (channel != null)
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
As UDP is an unreliable protocol, you have to be careful not to overload your system or your network buffers or packets will be lost. How to do this is likely to be sensitive to a number of factors, so it doesn't surprise me that doing this a little differently could improve your response rate.
Are you using UDP Datagrams or TCP Sockets? Why are trying to poll 11 million names servers?