Java asynchronous client, unexpected behaviour - java

I opened recently one of my asynchronous socket snippets, and I was amazed by the buggy behavior of the following client code, it is a basic PoC client using AsynchronousSocketChannel.
This snippet ideally, it should never reach the "I am freaking out", but it does.
Basically the problem is I use a ByteBuffer which at the end of the loop I set its position to 0, at the beginning of the loop I expect it to be 0, but SOMETIMES it is not.
The video showing of the bug is:
https://www.youtube.com/watch?v=bV08SjYutRw&feature=youtu.be
I can solve the problem calling .clear() just after .nextLine() but, I feel curious, what is going on in this innocent snippet?
package com.melardev.sockets.clients;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Scanner;
public class AsyncTcpClientCallbacks {
// Horrible demo, I would never write asynchronous sockets this way, I would use attachments
// which allows our code to be cleaner and isolate everything into their own classes
// This is only here to show you how you could do it without attachments, but you have
// to expose the socketChannel so it can be accessible from everywhere, my recommendation is not to bother
// learning, this, go to the demo where I use attachments, it is a lot more readable
static CompletionHandler<Integer, ByteBuffer> readHandler = new CompletionHandler<Integer, ByteBuffer>() {
#Override
public void completed(Integer bytesReaded, ByteBuffer buffer) {
buffer.flip();
byte[] receivedBytes = new byte[buffer.limit()];
// Get into receivedBytes
buffer.get(receivedBytes);
String message = new String(receivedBytes);
System.out.println(message);
buffer.clear();
socketChannel.read(buffer, buffer, this);
}
#Override
public void failed(Throwable exc, ByteBuffer buffer) {
System.err.println("Error reading message");
System.exit(1);
}
};
static private CompletionHandler<Integer, Void> writeHandler = new CompletionHandler<Integer, Void>() {
#Override
public void completed(Integer bytesWritten, Void attachment) {
}
#Override
public void failed(Throwable exc, Void attachment) {
System.err.println("Something went wrong");
System.exit(-1);
}
};
private static AsynchronousSocketChannel socketChannel;
public static void main(String[] args) {
try {
socketChannel = AsynchronousSocketChannel.open();
//try to connect to the server side
socketChannel.connect(new InetSocketAddress("localhost", 3002), null
, new CompletionHandler<Void, Void>() {
#Override
public void completed(Void result, Void attachment) {
ByteBuffer receivedBuffer = ByteBuffer.allocate(1024);
socketChannel.read(receivedBuffer, receivedBuffer, readHandler);
Scanner scanner = new Scanner(System.in);
System.out.println("Write messages to send to server");
ByteBuffer bufferToSend = ByteBuffer.allocate(1024);
String line = "";
while (!line.equals("exit")) {
if (bufferToSend.position() != 0) {
System.err.println("I am freaking out 1");
}
line = scanner.nextLine();
if (bufferToSend.position() != 0) {
System.err.println("I am freaking out 2");
}
byte[] bytesToWrite = line.getBytes();
// bufferToSend.clear();
bufferToSend.put(bytesToWrite);
System.out.println(bufferToSend.limit());
bufferToSend.flip();
System.out.println(bufferToSend.limit());
if (bufferToSend.position() != 0) {
System.err.println("I am freaking out 3");
}
if (bufferToSend.limit() != line.length()) {
System.err.println("I am freaking out 4");
}
socketChannel.write(bufferToSend, null, writeHandler);
bufferToSend.limit(bufferToSend.capacity());
bufferToSend.position(0);
// The two lines above are the same as
// bufferToSend.clear(); // Reuse the same buffer, so set pos=0
// limit to the capacity which is 1024
if (bufferToSend.position() != 0) {
System.err.println("I am freaking out 5");
}
}
}
#Override
public void failed(Throwable exc, Void nothing) {
System.out.println("Error connection to host");
}
});
while (true) {
try {
Thread.sleep(60 * 1000);
// Sleep 1 min ... who cares ?
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
How on earth the "I am freaking out" statements are executed? I see no way it could be, the conditions should never be evaluated to true, I made the video where the bug is clearly shown, sometimes at the beginning of the while() loop the position of the buffer is different than 0, but it should not, because at the end of the loop I set it to 0.
SURPRISINGLY ENOUGH, this behavior DOES NOT occur, when I make a breakpoint before launching the app, and I trace line by line ... how could it be?
I recorded it, I began with tracing through the debugger, everything worked fine, but once I removed the breaking and let the debugger run, the same code that worked before, now it does not. What am I missing?
The video showing when it worked with tracing is here:
https://www.youtube.com/watch?v=0H1OJdZO6AY&feature=youtu.be
If you wanna a server to play with, then this is the one used in the video
package com.melardev.sockets.servers;
import com.melardev.sockets.Constants;
import java.io.IOException;
import java.net.InetAddress;
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 AsyncTcpEchoServerKey {
public static void main(String[] args) {
try {
// Create new selector
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
// By default this is true, so set it to false for nio sockets
serverSocketChannel.configureBlocking(false);
InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
// Bind to localhost and specified port
serverSocketChannel.socket().bind(new InetSocketAddress(loopbackAddress, Constants.SOCKET_PORT));
// ServerSocketChannel only supports OP_ACCEPT (see ServerSocketChannel::validOps())
// it makes sense, server can only accept sockets
int operations = SelectionKey.OP_ACCEPT;
serverSocketChannel.register(selector, operations);
while (true) {
if (selector.select() <= 0) {
continue;
}
try {
processReadySet(selector.selectedKeys());
} catch (IOException e) {
continue;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void processReadySet(Set readySet) throws IOException {
Iterator iterator = readySet.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
// After processing a key, it still persists in the Set, we wanna remove it
// otherwise we will get it back the next time processReadySet is called
// We would end up processing the same "event" as many times this method is called
iterator.remove();
System.out.printf("isAcceptable %b isConnectable %b isReadable %b isWritable %b\n"
, key.isAcceptable(), key.isConnectable(), key.isReadable(), key.isWritable());
if (key.isAcceptable()) {
ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
// Get the client socket channel
SocketChannel clientSocketChannel = (SocketChannel) ssChannel.accept();
// Configure it as non-blocking socket
clientSocketChannel.configureBlocking(false);
// Register the socket with the key selector, we want to get notified when we have
// something to read from socket(OP_READ)
clientSocketChannel.register(key.selector(), SelectionKey.OP_READ);
} else if (key.isReadable()) {
// A Remote client has send us a message
String message = "nothing";
// Get the socket who sent the message
SocketChannel sender = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesCount = 0;
try {
bytesCount = sender.read(buffer);
if (bytesCount > 0) {
// 1. Get manually
message = new String(buffer.array(), 0, bytesCount);
// 2. Or, use flip
// set buffer.position =0 and buffer.limit = bytesCount
buffer.flip();
byte[] receivedMessageBytes = new byte[bytesCount];
buffer.get(receivedMessageBytes);
message = new String(receivedMessageBytes);
System.out.println("Receive " + message);
// Writing
// 1. Easy approach, create a new ByteBuffer and send it
// ByteBuffer outputBuffer = ByteBuffer.wrap(message.getBytes());
// sender.write(outputBuffer);
// 2. Or to reuse the same buffer we could
// buffer.limit(buffer.position());
// buffer.position(0);
// 3. Or the same as point 2, but one line
buffer.flip();
sender.write(buffer);
} else {
SocketChannel ssChannel = (SocketChannel) key.channel();
ssChannel.close();
System.out.println("Client disconnected");
break;
}
} catch (IOException e) {
e.printStackTrace();
try {
sender.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}
PD: The first video shows even more than what I have said before, notice that before setting the breaking the console showed I am freaking out 2 and 4, when the breaking was set, I also triggered I am freaking out 1, which at the beginning it wasn't, not only that, but when I resumed the process, this time, I am freaking out 1 was not triggered!!!

The documentation of AsynchronousSocketChannel.write() says:
Buffers are not safe for use by multiple concurrent threads so care should be taken to not access the buffer until the operation has completed.
You don't take such care, rather you set bufferToSend's limit and position regardless of the write completion, so the unexpected behavior may be due to this carelessness.

Related

NIO chat application not working properly for multiple clients

I've been working on a NIO-based chat application of quite trivial logic: any message sent by any client should be visible to the rest of the users.
Right now, I'm sort of in the middle of the work, I've got pretty complete classes of the clients (and their GUI part) and the server but I've stumbled on a problem I couldn't find any solution on anywhere. Namely, if I run an instance of the server and one instance of the client, in my consoles (one for client, one for the server) I see a nice, expected conversation. However, after adding additional client, this newly created client doesn't get responses from the server - the first still has a valid connection.
I'm not thinking about broadcasting messages to all the clients yet, now I'd like to solve the problem of the lack of proper communication between each of my clients and the server since, I think that broadcasting shouldn't be so big a deal if the communication is fine.
I'd like to also add that I've tried many other ways of instantiating the clients: in one thread, firstly instantiating the clients then applying methods on them, I've event tried using invokeLater from SwingUtilities, since that's the proper way to boot up GUI. Sadly, neither worked.
What should I change to achieve proper communication between clients and the server? What am I doing wrong?
This is the log from client console:
Awaiting message from: client2...
Awaiting message from: client1...
after creating the clients - before any action
1 Message: client1 :: simpleMess1
2 started pushing message from: client1
3 Server response on client side: ECHO RESPONSE: client1 :: simpleMess1
4 Message: client2 :: simpleMessage from c2
5 started pushing message from: client2
6
7 -- No response from client2. AND next try from client2 shows no log at all (!)
8
9 Message: client1 :: simple mess2 from c1
10 started pushing message from: client1
11 Server response on client side: ECHO RESPONSE: client1 :: simpleMess1
And the log from server side console:
1 Server started...
2 S: Key is acceptable
3 S: Key is acceptable
4
5 -- after creating the clients before any action
6 S: Key is readable.
The console output clearly shows that the server receives acceptable keys from both clients but it suggest also that only one SocketChannel has a SelectionKey of readable type, but I've got no clue why. Moreover, I think that the order of creating the clients doesn't matter because as I tested: the client that talks properly with the server is always the one that starts communication as first.
Below I'm posting my Server and Client classes code, hoping You'll Guys help me sort it out.
Firstly, Server class:
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class Server {
private ServerSocketChannel serverSocketChannel = null;
private Selector selector = null;
private StringBuffer messageResponse = new StringBuffer();
private static Charset charset = Charset.forName("ISO-8859-2");
private static final int BSIZE = 1024;
private ByteBuffer byteBuffer = ByteBuffer.allocate(BSIZE);
private StringBuffer incomingClientMessage = new StringBuffer();
Set<SocketChannel> clientsSet = new HashSet<>();
public Server(String host, int port) {
try {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(host, port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
catch (Exception exc) {
exc.printStackTrace();
System.exit(1);
}
System.out.println("Server started...");
serviceConnections();
}
private void serviceConnections() {
boolean serverIsRunning = true;
while (serverIsRunning) {
try {
selector.select();
Set keys = selector.selectedKeys();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = (SelectionKey) iter.next();
iter.remove();
if (key.isAcceptable()) {
System.out.println("\tS: Key is acceptable");
SocketChannel incomingSocketChannel = serverSocketChannel.accept();
incomingSocketChannel.configureBlocking(false);
incomingSocketChannel.register(selector, SelectionKey.OP_READ);
clientsSet.add(incomingSocketChannel);
continue;
}
if (key.isReadable()) {
System.out.println("\tS: Key is readable.");
SocketChannel incomingSocketChannel = (SocketChannel) key.channel();
serviceRequest(incomingSocketChannel);
continue;
}
}
}
catch (Exception exc) {
exc.printStackTrace();
continue;
}
}
}
private void serviceRequest(SocketChannel sc) {
if (!sc.isOpen()) return;
incomingClientMessage.setLength(0);
byteBuffer.clear();
try {
while (true) {
int n = sc.read(byteBuffer);
if (n > 0) {
byteBuffer.flip();
CharBuffer cbuf = charset.decode(byteBuffer);
while (cbuf.hasRemaining()) {
char c = cbuf.get();
if (c == '\r' || c == '\n') break;
incomingClientMessage.append(c);
}
}
writeResp(sc, "ECHO RESPONSE: " + incomingClientMessage.toString());
}
}
catch (Exception exc) {
exc.printStackTrace();
try {
sc.close();
sc.socket().close();
}
catch (Exception e) {
}
}
}
private void writeResp(SocketChannel sc, String addMsg)
throws IOException {
messageResponse.setLength(0);
messageResponse.append(addMsg);
messageResponse.append('\n');
ByteBuffer buf = charset.encode(CharBuffer.wrap(messageResponse));
sc.write(buf);
}
//second version - with an attempt to acomlish broadcasting
private void writeResp(SocketChannel sc, String addMsg)
throws IOException {
messageResponse.setLength(0);
messageResponse.append(addMsg);
messageResponse.append('\n');
ByteBuffer buf = charset.encode(CharBuffer.wrap(messageResponse));
System.out.println("clientsSet: " + clientsSet.size());
for (SocketChannel socketChannel : clientsSet) {
System.out.println("writing to: " + socketChannel.getRemoteAddress());
socketChannel.write(buf);
buf.rewind();
}
}
public static void main(String[] args) {
try {
final String HOST = "localhost";
final int PORT = 5000;
new Server(HOST, PORT);
}
catch (Exception exc) {
exc.printStackTrace();
System.exit(1);
}
}
}
and the Client class:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
private ClientView clientView;
private String hostName;
private int port;
private String clientName;
private Socket socket = null;
private PrintWriter printWriterOUT = null;
private BufferedReader bufferedReaderIN = null;
public Client(String hostName, int port, String clientName) {
this.hostName = hostName;
this.port = port;
this.clientName = clientName;
initView();
}
public void handleConnection() {
try {
socket = new Socket(hostName, port);
printWriterOUT = new PrintWriter(socket.getOutputStream(), true);
bufferedReaderIN = new BufferedReader(new InputStreamReader(socket.getInputStream()));
waitForIncomingMessageFromClientView();
bufferedReaderIN.close();
printWriterOUT.close();
socket.close();
}
catch (UnknownHostException e) {
System.err.println("Unknown host: " + hostName);
System.exit(2);
}
catch (IOException e) {
System.err.println("I/O err dla");
System.exit(3);
}
catch (Exception exc) {
exc.printStackTrace();
System.exit(4);
}
}
public void initView() {
clientView = new ClientView(clientName);
}
public void waitForIncomingMessageFromClientView() {
System.out.println("Awaiting message from: " + clientName + "...");
while (true) {
if (clientView.isSent) {
System.out.println("Message: " + clientView.getOutgoingMessage());
pushClientViewMessageToServer();
clientView.setIsSent(false);
}
}
}
public void pushClientViewMessageToServer() {
String clientViewMessage = clientView.getOutgoingMessage();
System.out.println("started pushing message from: " + clientView.getClientName());
try {
printWriterOUT.println(clientViewMessage);
String resp = bufferedReaderIN.readLine();
System.out.println("Server response on client side: " + resp);
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
#Override
public void run() {
Client c1 = new Client("localhost", 5000, "client1");
c1.handleConnection();
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
#Override
public void run() {
Client c2 = new Client("localhost", 5000, "client2");
c2.handleConnection();
}
});
thread2.start();
}
}
I'll apprecaite any help from You Guys.
EDIT:
the second version of writeResp method attempting to broadcast echo to all the clients produces such log:
Server started...
clientsSet: 2
writing to: /127.0.0.1:63666
writing to: /127.0.0.1:63665
clientsSet: 2
writing to: /127.0.0.1:63666
writing to: /127.0.0.1:63665
It seems like there are two clients and I'm wondering why they don't get proper reply from the server.
while (true) {
int n = sc.read(byteBuffer);
if (n > 0) {
byteBuffer.flip();
CharBuffer cbuf = charset.decode(byteBuffer);
while (cbuf.hasRemaining()) {
char c = cbuf.get();
if (c == '\r' || c == '\n') break;
incomingClientMessage.append(c);
}
}
There is a major problem here. If read() returns -1 you should close the SocketChannel, and if it returns -1 or zero you should break out of the loop.

Continuously read objects from an ObjectInputStream in Java

I have a problem using an ObjectInputStream and I have been struggling with it for 2 days now. I tried to search for a solution but unfortunately found no fitting answer.
I am trying to write a client/server application in which the client sends objects (in this case a configuration class) to the server. The idea is that connection keeps alive after sending the object so it is possible to send a new object if necessary.
Here are the important parts of my client code:
mSocket = new Socket("192.168.43.56", 1234);
mObjectIn = new ObjectInputStream(mSocket.getInputStream());
mObjectOut = new ObjectOutputStream(mSocket.getOutputStream());
mObjectOut.writeObject(stubConfig);
mObjectOut.flush();
In the above code, I left out some try/catch blocks to keep the code readable for you.
The server side looks as follows:
mHostServer = new ServerSocket(port);
mSocket = mHostServer.accept();
// create streams in reverse oreder
mObjectOut = new ObjectOutputStream(mConnection.getOutputStream());
mObjectOut.flush();
mObjectIn = new ObjectInputStream(mConnection.getInputStream());
while (mIsSocketConnected)
{
StubConfig = (StubConfiguration)mObjectIn.readObject();
}
What I want to achieve is that as long at the socketconnection is alive, the server is listening for incoming config objects.
When I run my program however, I got an EOFException in the while loop at server side. I receive the first config object without any problems in the first iteration of the while loop but after that I get an EOFException every time readObject() is called.
I am looking for a way to solve this. Can anyone put me in the good direction?
EDIT: What I read about the EOFException is that it is thrown when you want to read from a stream when the end of it is reached. That means that for some reason the stream ended after the object has been send. Is there a way to reinitialize the streams or so??
EOFException is thrown by readObject() when the peer has closed the connection. There can never be more data afterwards. Ergo you can't have written multiple objects at all: you closed the connection instead.
try using this
Server side
1.Server running on a separate thread
public class ServeurPresence implements Runnable {
public final static int PORT = 20000 ;
public final static String HOSTNAME = "localhost" ;
public static enum Action {CONNEXION, MSG, DECONNEXION,USER, FINCLASSEMENT};
ServerSocket serveur ;
static List<String> names ;
*/
public ServeurPresence()
{
System.out.println("Start Server...");
try
{
serveur = new ServerSocket(PORT) ;
new Thread(this).start();
//javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI();} } );
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* #param args
*/
public static void main(String[] args)
{
new ServeurPresence();
}
#Override
public void run()
{
System.out.println("server runs");
while(true)
{
try {
Socket sock = serveur.accept();
ServiceClientsThread thread= new ServiceClientsThread(sock);
thread.start();
}
catch (IOException e)
{
System.out.println("Error with socket");
e.printStackTrace();
}
}
}
}
2. A Thread to handle each Client:ServiceClientThread
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ServiceClientsThread extends Thread{
private Socket sock ;
ServiceClientsThread(Socket sock)
{
//super();
this.sock=sock;
}
#Override
public void run()
{
DataInputStream is ;
DataOutputStream os ;
String name =null ;
try {
is = new DataInputStream(sock.getInputStream()) ;
os = new DataOutputStream(sock.getOutputStream()) ;
ServeurPresence.Action act ;
do {
// read Action
act = ServeurPresence.Action.valueOf(is.readUTF()) ; // read string -> enum
System.out.println("action :"+act);
switch (act) {
case CONNEXION :
name = is.readUTF(); //read client name
System.out.println("Name :"+name);
os.writeUTF("Hi");//send welcome msg
break ;
case MSG :
String msg = is.readUTF();
os.writeUTF("OK");//response
break ;
case DECONNEXION :
System.out.println(name+" is logged out");
break ;
}
} while (act!=ServeurPresence.Action.DECONNEXION) ;
// the end
is.close();
os.close();
sock.close();
} catch (IOException e)
{
System.out.println("Error with "+name+" socket");
e.printStackTrace();
}
}
}
3. Client side
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
/**
*
*/
Client(String name)
{
System.out.println("Start Client...");
try {
Socket sock = new Socket(ServeurPresence.HOSTNAME,ServeurPresence.PORT) ;
DataOutputStream os = new DataOutputStream(sock.getOutputStream()) ;
DataInputStream is = new DataInputStream(sock.getInputStream()) ;
System.out.println("Send "+name+" to server");
// CONNECTION : Action then value
os.writeUTF(ServeurPresence.Action.CONNEXION.name()) ; // send action : write enum -> String
os.writeUTF(name) ; // send the name
//read server welcome msg
String msg = is.readUTF();
System.out.println("Welcome msg: "+msg);
/* Your actions here : see example below */
try
{
Thread.currentThread().sleep(4000);
os.writeUTF(ServeurPresence.Action.MSG.name()) ; // send action : write enum -> String
os.writeUTF("My message here") ; // send msg
Thread.currentThread().sleep(4000);
msg = is.readUTF();//server response message
}
catch (InterruptedException e)
{
e.printStackTrace();
}
/************************************************/
//CLOSE
os.writeUTF(ServeurPresence.Action.DECONNEXION.name()) ; // send action
System.out.println("Log out");
os.close();
sock.close();
}
catch (UnknownHostException e)
{
System.out.println(ServeurPresence.HOSTNAME+ " unknown");
e.printStackTrace();
}
catch (IOException e)
{
System.out.println("Impossible to connect to "+ServeurPresence.HOSTNAME+ ":"+ServeurPresence.PORT);
e.printStackTrace();
}
}
}
4. In your case use readObject()/writeObject() instead of readUTF()/writeUTF() to write your config objects
Try this and let me know how it goes:
while (1==1)
{
StubConfig = (StubConfiguration)mObjectIn.readObject();
Thread.sleep(100); //Saves CPU usage
}
Very late answer, but just for future reference. I have been having problems sending Objects via sockets because the method flush() is not working properly.
I solved this problem just by switching flush() to reset().

Transmitting data from another thread, slow serial link with Java and RXTX library

Alright, I'll try to be as clear as possible with my problem.
I'm transmitting serial data over a veeeeeeery slow radio link (using the UART-controller on the raspberry pi and a home-built radio). It's for a very specific project where the requirement is spelled long range and speed is of less importance.
The program (Radio.java) is running two threads. One thread (Receiver) receives telemetry data from another program using a TCP-socket (which is very high speed, actually 100mbit). This thread continuously saves the data it receives on the TCP-socket in an ArrayBlockingQueue (with size = 1) so that the other thread (Transmitter) can reach this data. The rate at which the Receiver-thread receives data is pretty high. Now, I want Transmitter-thread to transmit the data and when it's finished I want it to again get the latest data from Receiver-thread and transmit it again over the slow-radio-link.
So in transmitter-thread I want it to work like this:
Get latest data from Receiver-thread
Transmit data over radio link (using the serialport)
Don't do ANYTHING until the data is actually transmitted.
repeat.
Now, when I'm running the program everything regarding the Receiver-thread is working just fine. But inside the transmitter-thread the line "this.out.write(output.getBytes());" just puts everything inside the OutputStream in a couple of milliseconds, and then again does the same thing. The data has no chance in being transmitted!
I've tried the example (only using the "SerialWriter"-thread) here:
http://rxtx.qbang.org/wiki/index.php/Two_way_communcation_with_the_serial_port
And using a long "Lirum Ipsum"-text everything worked just fine transmitting in 50baud. So basically, I want the same behaviour in my program as using System.in.read > -1... (which I guess is blocking, the reason it works???).
What should I do?
2015-01-01 edit BEGIN
I've found the problem! SRobertz lead me into the right direction! The problem is actually not the writespeed to the UART-buffer. The difference between running the "TwoWayComm"-example and my own code is that I'm running a GPS connected to UART-RX-port of the Raspberry Pi. To read data from the GPS is use the "GPSD"-software (which outputs data in a JSON-format). The GPSD-software connects to the GPS with 9600baud (specifically for this GPS-unit), while I switch to 50 baud on the same port (without closing the open connection that GPSD is running)! Trying to open UART with two different baud-rates is what is messing everything up.
I've rewritten the code so that I:
Open UART on 9600 baud
Read GPS data
Close the UART
Open UART on 50 baud
Transmit telemetry data to UART
Close UART
Repeat
And now everything works like a charm...
2015-01-01 edit END
So ... here is the code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
public class RADIO {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
void connect(String portName) throws Exception {
CommPortIdentifier portIdentifier = CommPortIdentifier
.getPortIdentifier(portName);
if (portIdentifier.isCurrentlyOwned()) {
System.out.println("Error: Port is currently in use");
} else {
int timeout = 2000;
CommPort commPort = portIdentifier.open(this.getClass().getName(),
timeout);
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(50, SerialPort.DATABITS_7,
SerialPort.STOPBITS_2, SerialPort.PARITY_NONE);
// Open outputstream to write to the serial port
OutputStream out = serialPort.getOutputStream();
(new Thread(new Receiver(queue))).start();
(new Thread(new Transmitter(out, queue))).start();
} else {
System.err.println("Error: Not serial port.");
}
}
}
public static class Receiver implements Runnable {
OutputStream out;
protected ArrayBlockingQueue<String> queue = null;
public Receiver(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
public void run() {
// Open TCP-connection
try {
ServerSocket serverSocket = new ServerSocket(1002);
Socket clientSocket = serverSocket.accept(); // Wait for the client to start up
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
String inputLine, outputLine;
while ((inputLine = in.readLine()) != null) {
queue.clear();
queue.put(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static class Transmitter implements Runnable {
OutputStream out;
protected ArrayBlockingQueue<String> queue = null;
String output = "";
public Transmitter(OutputStream out, ArrayBlockingQueue<String> queue) {
this.out = out;
this.queue = queue;
}
public void run() {
try {
while (true) {
output = queue.take();
this.out.write(output.getBytes());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
(new RADIO()).connect("/dev/ttyAMA0");
} catch (Exception e) {
e.printStackTrace();
}
}
}
(a small caveat: I haven't used gnu.io on the raspberry pi)
First, to isolate the problem, I would put a fairly long sleep in the transmitter thread, after this.out.write... to verify that the problem is not waiting for the serial port to
finish the transmission.
If that works, then you can try waiting for OUTPUT_BUFFER_EMPTY, by adding a
SerialPortEventListener and setting notifyOnOutputEmpty(true), making your
SerialPortEventListener a monitor along the lines of
class ExampleMonitor implements SerialPortEventListener {
boolean condition;
public synchronized serialEvent(SerialPortEvent ev) {
condition = true;
notifyAll();
}
public synchronized void awaitCondition() throws InterruptedException {
while(!condition) wait();
condition = false;
}
and then do
myExampleMonitor.awaitCondition() instead of the sleep in the transmit thread.
See http://rxtx.qbang.org/wiki/index.php/Event_based_two_way_Communication for the inverse use of events (note that there, there is no monitor and no waiting; instead, the work is done in the listener/callback.)

SocketChannel not receiving data with Applet

I have this code which when i call from a standalone java application,it works well in that i can connect to the server send and receive data from the server successfully.
But when i use the same code in an applet,I can connect and send data but cannot receive data and am not getting any error message on either the server or the client.
They are both connecting to the same server application hence am eliminating issues with the server.
i have granted all permissions to the Applet
Your help will be highly appreciated
Main Application Code
public class NewJFrame extends javax.swing.JFrame {
/**
* Creates new form NewJFrame
*/
public NewJFrame() {
initComponents();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try
{
List<String> bb=new ArrayList<String>();
bb.add("Customer");
bb.add("ID");
byte [] serialized=ECSStreamUtil.serializeObject(bb);
ByteBuffer toSend=ByteBuffer.allocate(serialized.length);
toSend.put(serialized);
toSend.flip();
JavaApplication1.write(toSend);
toSend.clear();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static void main(String args[]) throws Exception{
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
new JavaApplication1();
JavaApplication1 code
package javaapplication1;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
public class JavaApplication1 {
static int x;
private static SocketChannel client ;
public JavaApplication1()throws Exception
{
client = SocketChannel.open();
// nonblocking I/O
client.configureBlocking(false);
// Connection to host port 4444
client.connect(new java.net.InetSocketAddress("localhost",4444));
// Create selector
Selector selector = Selector.open();
// Record to selector (OP_CONNECT type)
SelectionKey clientKey = client.register(selector, SelectionKey.OP_CONNECT);
// Waiting for the connection
while (true)
{
if(selector.select(5000)==0)return ;
// Get keys
Set keys = selector.selectedKeys();
Iterator i = keys.iterator();
// For each key...
while (i.hasNext())
{
SelectionKey key = (SelectionKey)i.next();
// Remove the current key
i.remove();
// Get the socket channel held by the key
SocketChannel channel = (SocketChannel)key.channel();
if(!channel.finishConnect())
return;
if(key.isConnectable())
{
SocketChannel sc=(SocketChannel)key.channel();
sc.register(selector,SelectionKey.OP_READ);
System.out.println("conne");
continue;
}
if(key.isReadable())
{
ByteBuffer buf=ByteBuffer.allocate(89);
int x=channel.read(buf);
if(x==-1)
{
key.cancel();
continue;
}
while((channel.read(buf)>0))
{
buf.flip();
}
byte c[]=buf.array();
System.out.println(new String(c));
//buf.clear();
}
}
keys.clear();
}
}
public static void write(ByteBuffer data)
{
try
{
client.write(data);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
Applet Code
private void jButton9ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try
{
List<String> bb=new ArrayList<String>();
bb.add("Customer");
bb.add("ID");
byte [] serialized=ECSStreamUtil.serializeObject(bb);
ByteBuffer toSend=ByteBuffer.allocate(serialized.length);
toSend.put(serialized);
toSend.flip();
JavaApplication1.write(toSend);
//Send information
}
catch(Exception io)
{
io.printStackTrace();
}
}
Am calling this code from the init method in applet
try
{
new JavaApplication1();
}
catch(Exception ex)
{
ex.printStackTrace();
}
Your client reading code is incorrect.
If read() returns -1 you should close the channel, not just cancel the key.
Your read/flip loop is nonsense. You should read until you have a complete message, however you determine that, then flip and get the data from the buffer.
I don't really see why you're using NIO in the client at all, when there is only one connection. There is really zero benefit. I would get it working with java.net in the client and then see whether you have any reason to change it.

Java Selector returns SelectionKey with OP_READ without data in infinity loop after writing to channel

I've trouble with my code: i've written simple SocketChannel client with Selector, after starting it successfully reads messages from server (server sends events). But after writing to socket (see main method) selector starts returning readable socket in infinyty loop, handleKey returns that -1 bytes readed, so selector all time returns OP_READ SelectionKey without data for reading.
Sorry for my English.
Thanks.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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;
public class SelectorTest
{
public SelectorTest() throws IOException {
selector = Selector.open();
}
private void runSelector() {
new Thread(new Runnable() {
public void run()
{
alive = true;
try {
while(alive) {
System.out.println("Selector started...");
selector.select();
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while(keyIter.hasNext()) {
SelectionKey key = keyIter.next();
keyIter.remove();
handleKey(key);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
}
private void handleKey(SelectionKey key) throws IOException {
SocketChannel chan = (SocketChannel) key.channel();
System.out.println("Processing selected...");
if(key.isConnectable()) {
System.out.println("Connecting ...");
if(chan.finishConnect()) {
key.interestOps(SelectionKey.OP_READ);
} else {
key.channel();
}
} else if(key.isReadable()) {
System.out.println("Processing reading...");
ByteBuffer buf = ByteBuffer.allocate(1024);
int readedBytes = chan.read(buf);
System.out.println("Readed: " + readedBytes);
buf.flip();
for(byte b : buf.array()) {
System.out.print((char) b);
}
} else if(key.isWritable()) {
System.out.println("Finishing writing...");
key.interestOps(SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws IOException {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("t1.sis.lan", 6001));
SelectorTest ds = new SelectorTest();
ds.runSelector();
channel.register(ds.selector, SelectionKey.OP_CONNECT);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for(;;) {
String line = in.readLine();
if(line==null) break;
if(line.toLowerCase().equals("bye")) break;
if (line.toLowerCase().equals("write")) {
String command = "GET_STREAMS\r\n\0";
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put(command.getBytes());
buf.flip();
channel.write(buf);
}
System.out.println("echo: "+line); // is it alive check
}
ds.alive = false;
ds.selector.wakeup();
channel.close();
}
private Selector selector;
private boolean alive;
}
read() returns -1 at EOS, which you are completely ignoring. When you get EOS, you must either close the channel or at least deregister interest in OP_READ. Otherwise you will just get another OP_READ and another -1 when you read, as you are doing, forever. Contrary to your comments above, read() returns zero on an empty read. You can ignore that, indeed you won't even see it if you only read when isReadable(), unless you read in a loop, but you must not ignore EOS.
read() returns -1 when it has read EOF. Definition:
read() returns: The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream
This means you should unregister the interest for OP_READ.

Categories

Resources