I have implmented JSSC API so I can communicate with the Com Port.
I send a command like "N\r\n"
and what i receive in a normal hyperterminal should look like this:
0100071CA79215021803164442180000
0100071C9F5415021803164514520000
0100071CDF5115022106142956600000
NOK
But when i do the same with the JSSC API i receive this (only the first code)
010
0071CA79
2150218
0316444
218
The Problem is that i randomly receive bit parts and at the end of the code i lose some parts. But thats not important i only need the first 12 digits of every code.
The Question is now how do i get the function to only receive the full line and not bitparts?
This is the receiving part of the class
class PortReader2 implements SerialPortEventListener {
#Override
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()&& event.getEventValue() > 2) {
try {
// получение ответа от порта
String receivedData = serialPort.readString();
System.out.println(receivedData.length() + ":" + receivedData);
}
catch (SerialPortException ex) {
System.out.println("Error in receiving response from port: " + ex);
}
}
}
}
This is the sending part
public void sendCodeCommand(SerialPort serialPort) {
// writing string to port
try {
Thread.sleep(3000);
serialPort.writeBytes("N\r\n".getBytes());
} catch (SerialPortException | InterruptedException ex) {
Logger.getLogger(ComPortSendReceive.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("String wrote to port, waiting for response..");
}
To fix the stated problem you need to concatenate the strings you receive into another string that accumulates the string fragments you receive from the readString() function, which is in the serialEvent handler. Because it is it's owm thread it gets a certain amount of cpu time to get serial data so it effectively gets partial sequential reads of the serial port input data. So this example puts the partial inputs together to create the "whole" input.
String serialString;
So within the serialEvent handler:
try {
serialStringFragment = serialPort.readString();
serialString.concat(serialStringFragment);
}
Either the string you get from readString() or the accumulation string can be scanned for tokens like eol condition. For Example:
String [] dlLines = serialString.split("\r\n");
will break each line out to an element in the dlLines string array.
However I have found that if I have fixed length output from my target device this works better:
serialPort.readString(int bytecount);
inline with the write serial string, eliminating the serialEvent handler.
This is a bit contrived but In other words:
String expectedStringLength "0100071CA79215021803164442180000";
int serialstrlen = expectedStringLength.length();
So the serialstrlen should obviously become constants for each expected line.
serialPort.writeString("N\r\n");
serialPort.readString(serialstrlen+2); // assuming cr lf
should get you the first line.
Put the readString() in a loop, change serialstrelen argument value according to the expected strings. Check the strings for alternate content for error handling. This has worked for me.
Related
I'm making a console chat application and now I have some problems with displaying messages on the client program.
My client program has two threads a read thread and write thread. The read thread gets messages from the server and write sends messages. The write thread has a Console object reading one line with a some text indicating to the user where to write your message. It looks like this in the console:
[Username]: message goes here
And this works fine when you only want to send messages, but I also have a read thread which is receiving and displaying messages that it got from the server. Which messes up this whole input thing because for example when the read thread gets a message it just prints it to the console and then prints another indicator on the next line and then everything looks like this:
[Username]:
[qwerty]: hello
[Username]:
I tried deleting the first indicator by printing \b (backspace) escape character in a for loop that looks like this:
String username = "username";
String indicator = "[" + username + "]: ";
for (int i = 0; i < indicator.length(); i++)
{
System.out.print("\b");
}
It worked on regular text, but not on the indicator. I think that's because the Console object reading for input in the same line somehow gets in the way.
This problem is even worse when the user is typing out a message and in the middle of the typing the user gets a message this happens:
[Username]: Hel //the user wanted to type out hello but got cut off
[qwerty]: hello
[Username]:
If the user where to finish writing hello on the next line and then send it. For the other client the message would say hello with no sign that the clients writing was interrupted by him, but for the client writing the message it would look like it sent two separate messages. I don't know how to fix this at all. Any help is appreciated.
Code for write thread:
public static class write implements Runnable
{
public void run()
{
Console cons = System.console();
while (true)
{
username = "[dgsgsdfg]: ";
System.out.print(username);
String input = cons.readLine();
String msg = username + input;
System.out.println(msg);
}
}
}
Code for read thread:
public static class read implements Runnable
{
public void run()
{
try
{
while(true)
{
Thread.sleep(5000);
for (int i = 0; i < username.length(); i++)
{
System.out.print("\b");
}
System.out.println("\n[qwerty]: hello");
System.out.print("[dgsgsdfg]: ");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
P.S: The code show here is from a different program I created to try and fix this problem and it only contains the text printing part, and no networking or server stuff. The writer thread every 5 seconds prints hello, to simulate receiving and displaying a message.
I am programming a little server-client-programm, which sends a text from one client who is writing on a file, to the other clients with the same filename, and got the following error
But I am just sending an integer and no other characters...
Here's the code:
Server
String[] splitter = scanText.split("\n");
String length = splitter.length + "";
//sending scanText to clients
for (PrintWriter pw2 : userMap.get(filename) ) {
if(!pw2.equals(pw))
{
pw2.println(length + "\n" + scanText);
}
}
Client
class "UpdateInBackground" is a class which is in the Client-class
class UpdateInBackground extends Thread {
#Override
public void run() {
int lines; //to know how much lines are send from the server
String scanText;
while (!this.isInterrupted()) {
scanText = "";
lines = Integer.parseInt(sc.nextLine()); //here I get the error
while (lines-- > 0) {
scanText += sc.nextLine() + "\n";
}
output.setText(scanText);
}
}
}
#asparagus, please define sc in line sc.nextLine(), considering this is an object from class Scanner, I need to know the input. The question must be self explainable with the definitions of variables and what are the inputs.
In Class UpdateInBackground,
lines = Integer.parseInt(sc.nextLine());// here nextLine() is for any String , please refer documentation
Reason for NumberFormatException : You are converting the value to int, without knowing, what is getting as input.
Try to use exception handling, to know what types of errors, might just come, to avoid the program getting struck.
I have wrote an event listener that suppose to read any messages of a specific type from a receiver.
here is my event listener:
class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if (event.isRXCHAR() && event.getEventValue() > 0) {
try {
byte[] buffer = SP.readBytes(6);
String type = new String(buffer);
if (type.equals("$GPGGA")){
String line = "";
line = line + type;
buffer = SP.readBytes(66);
String values = new String(buffer);
line = line + values;
writer.write(line + "\n");
}
}
catch (SerialPortException ex) {System.out.println(ex);}
catch (IOException ex) {System.out.println(ex);}
}
}
}
right now after i check that the message is of the correct type i just read 80 bytes off data, which is roughly the size of the message.
however, sometimes the message is not full and there for it is shorter then usual.
this is the message structure:
as you can see, the message end s with <CR><FL>.
i would like to modify my method so it would read each byte at a time and stop reading once it hits the end of the message. how can i catch the <CR><FL> inside the byte array?
any help would be appreciated. thank you.
It depends on the definition of "messages": are they fixed-length or delimited by a certain sequence? In your current code, you're reading a "header" that is 6 bytes long and a "message" that is 66 bytes long. However, it appears that you actually don't know the length a priori, and instead the message is newline-terminated. A couple of pointers:
You're reading bytes from the stream, then turning them into a String by using the String(byte[]) ctor. The documentation states that this uses the default charset for your platform, which may be UTF-8, Latin-1 or whatever regional default. If you are communicating with a device over a serial port, this is probably not what you want since the device is likely to have a single, specific charset for messages (maybe ASCII?). Investigate that point and use either this String ctor or, if you want to be notified when the input contains undecodable garbage, the CharsetDecoder class.
If the messages are text-based and newline-delimited, you should definitely use a BufferedReader to read from the stream. Assuming that your SP serial port is an InputStream, you could create a BufferedReader over it, and then call the readLine() method on the reader. The reader will keep requesting bytes from the stream until it sees a newline, then return the whole line to you as a String. Reader objects also encapsulate the Charset I was talking about before.
I have managed to solve my problem.
pretty easy solution:
String tmp = SP.readString();
String[] msgs = tmp.split("\r\n");
after reading the string from the serial port, i just split it by the end of line.
I'm implementing a simple server using AsynchronousServerSocketChannel. For testing purposes, I created a tiny client prototype that sends two messages, "hi" and "stackoverflow", then disconnects. On server side, I read the arrived messages and print them to standard output. When the client executed, I'm expecting to receive:
message [hi], bytecount 2
message [stackoverflow], bytecount 13
The problem is, that sometimes both messages already arrived when server invokes reading callback so I get
message [histackoverflow], bytecount 15
instead.
The question is, if it is possible to ensure on server side that the messages arrive separately and if yes, how to do it?
Here's my CompletionHandler prototype that handles client connections:
class CommunicationHandler implements CompletionHandler<AsynchronousSocketChannel, Void> {
private final AsynchronousServerSocketChannel server;
public CommunicationHandler(final AsynchronousServerSocketChannel server) {
this.server = server;
}
#Override
public void failed(Throwable ex, Void attachment) {}
#Override
public void completed(final AsynchronousSocketChannel client, Void attachment) {
// handle client messages
final ByteBuffer buffer = ByteBuffer.allocateDirect(Server.BUFFER_SIZE);
final Session session = new Session();
try {
client.read(buffer, session, new CompletionHandler<Integer, Session>() {
#Override
public void completed(Integer byteCount, final Session currSession) {
if (byteCount == -1) {
return;
}
buffer.flip();
// TODO forward buffer to message handler (probably protocol?)
System.out.println("message [" + convertToString(buffer) + "], byteCount " + byteCount);
buffer.clear();
// read next message
client.read(buffer, currSession, this);
}
#Override
public void failed(Throwable ex, final Session currSession) {}
});
}
// accept the next connection
server.accept(null, this);
}
ByteBuffer to String conversion:
public static String convertToString(ByteBuffer bb) {
final byte[] bytes = new byte[bb.remaining()];
bb.duplicate().get(bytes);
return new String(bytes);
}
Here is a test client prototype:
public class Client {
public final void start() {
try (AsynchronousSocketChannel client = AsynchronousSocketChannel.open();) {
Future<Void> connCall = client.connect(InetAddress.getByName("127.0.0.1"), 8060));
connCall.get();
// client is now connected
// send greeting message
Future<Integer> writeCall = client.write(Charset.forName("utf-8").encode(CharBuffer.wrap("hi")));
writeCall.get();
// Thread.sleep(5000L);
writeCall = client.write(Charset.forName("utf-8").encode(CharBuffer.wrap("stackoverflow")));
writeCall.get();
client.close();
} catch (IOException e) {
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
}
}
In addition to the possibility of getting two (or even more) writes in one read, for larger messages (usually about 3k or more) you can get one write split over several reads. TCP is a stream protocol and does not preserve record boundaries, unless by chance: What is a message boundary? There are two solutions that work in general, although with async channel I think you'll need to do your own buffer management which may be confusing and hard to test:
add an explicit length field before each record
add a delimiter after each record when there is a byte not otherwise used, or an escape can be used to distinguish data from the delimiter
and several others that have been tried:
as your comment suggests, wait long enough that the first request has always been read before the second is sent. On the local networks and test systems used by developers this usually is a few milliseconds or even less; on the real Internet it is fairly often several seconds, sometimes minutes, and in theory can be hours or even days.
if records are never longer than a few fragments (maybe 10k or so) use UDP (available in Java as DatagramSocket but not as a NIO channel AFAICS) and implement your own protocols to handle message loss, duplication and reordering (which is hard to do and often ends up failing in some obscure cases that were discovered and avoided or fixed in TCP 30 years ago)
use SCTP (not available in Java at all AFAICS, and not too many other systems either)
Aside: your test client sends data in UTF-8, but new String (byte[]) uses the default encoding which is platform-dependent and not necessarily UTF-8. I'm not sure it's guaranteed but in practice all usable encodings include ASCII as a subset, and your example data is ASCII. But if you want to support actual UTF-8 data code for it.
I want to write a simple web proxy, for exercise. Here's the code I have so far:
def g = new Proxy()
g.serverPort = 9000
println "starting"
g.eachClient { Socket client ->
println "got a client"
try {
client.withStreams { input,output ->
String text = input.text
println "received $text from client"
client.close()
}
} catch(IOException io) {
println "IO error. Probably client disconnected"
//io.printStackTrace()
}
}
the thing is, the line :
String text = input.text
consumes all the available data in the Socket's InputStream. If the client isn't closing the connection, that method will just wait until it can read a end of file character ( if I remember correctly ). What do I have to prevent this from happening, and have the client's string available ASAP?
I think you'll want to check the documentation on ObjectInputStream. Do length = input.available to get the number of available bytes at the present time, then use input.read(buffer, offset, length) to read in exactly as many bytes are available. You'll probably want to launch a new thread for every new connection which transparently manages this buffer in the background, unless you're making a single-threaded proxy to begin with.