Replace nio sockets to exec'ed software for binary protocol - java

I have to make an abstaction in my software - replace direct unblockable NIO sockets ( client/server ) to software abstraction.
For example, instead of connecting via tcp client would exec openssl s_client -connect xxx.xxx.xxx.xxx . I have written a little demo, and it even works. Sometimes :(
The first trouble is that Process's streams can't be used with Selector, so I can't replace socketchannel with any other type of channel, so I have to read/write without any chance to avoid blocking.
The second one is that a protocol is a duplex binary file-transfer protocol ( binkp ), so process's buffered streams are unusabe. I've tried to avoid that converting in/out data to base64 and it works, but also sometimes.
I can't understant why it works or not sometimes. I put a piece of test code below. The first word is frame's length, but first bit is ignored. Please, tell me your guesses. Thanks.
public class BufferedSocketBase64 {
static class InToOut implements Runnable {
InputStream is;
OutputStream os;
boolean direction; //
public InToOut(InputStream is, OutputStream os, boolean direction) {
super();
this.is = is;
this.os = os;
this.direction = direction;
}
#Override
public void run() {
System.out.println(Thread.currentThread().getId() + " start "
+ ((direction) ? "encode from to" : "decode from to"));
boolean eof = false;
while (true) {
if (direction) {
// encode to base64 data
try {
int[] head = new int[2];
for (int i = 0; i < 2; i++) {
head[i] = is.read();
}
int len = (head[0] & 0xff << 8 | head[1] & 0xff) & 0x7FFF;
byte[] buf = new byte[len + 2];
buf[0] = (byte) (head[0] & 0xff);
buf[1] = (byte) (head[1] & 0xff);
for (int i = 2; i < len; i++) {
buf[i] = (byte) (is.read() & 0xff);
}
System.out.println(Thread.currentThread()
.getId() + " << " + new String(buf));
if (len > 0) {
String send = Base64Util.encode(buf, len);
send += "\n";
os.write(send.getBytes());
os.flush();
}
} catch (IOException e) {
eof = true;
}
} else { // decode from base64
try {
StringBuilder sb = new StringBuilder(1024);
byte c = 0x0a;
do {
c = (byte) is.read();
if (c >= 0 && c != 0x0a) {
sb.append(new String(new byte[] { c }));
}
} while (c != 0x0a && c >= 0);
if (sb.length() != 0) {
try {
byte[] buf = Base64Util.decode(sb.toString());
System.out.println(Thread.currentThread()
.getId() + " >> " + buf.length);
os.write(buf);
os.flush();
} catch (StringIndexOutOfBoundsException e) {
System.out
.println(Thread.currentThread().getId()
+ " error on " + sb.toString());
}
}
} catch (IOException e) {
eof = true;
}
}
if (eof) {
System.out.println(Thread.currentThread().getId() + " EOF");
break;
}
}
try {
is.close();
os.close();
} catch (IOException e) {
}
}
}
public static void main(String[] args) throws Exception {
Process proc2 = Runtime.getRuntime().exec("nc -l -p 2020");
Process proc1 = Runtime.getRuntime().exec("nc 127.0.0.1 2020");
Socket sock1 = new Socket();
sock1.connect(new InetSocketAddress("127.0.0.1", 24554), 30);
Socket sock2 = new Socket();
sock2.connect(new InetSocketAddress("127.0.0.1", 24557), 30);
new Thread(new InToOut(sock1.getInputStream(), proc1.getOutputStream(),
true)).start();
new Thread(new InToOut(proc1.getInputStream(), sock1.getOutputStream(),
false)).start();
new Thread(new InToOut(sock2.getInputStream(), proc2.getOutputStream(),
true)).start();
new Thread(new InToOut(proc2.getInputStream(), sock2.getOutputStream(),
false)).start();
}
UPDATED:
I've found right way. I uses syncchronized queries for each stream and synchronized threads to fill or erase that queries. All threads mutually blocks themselves. And it works! :)
Sorry for bother.

I've found right way. I uses syncchronized queries for each stream and synchronized threads to fill or erase that queries. All threads mutually blocks themselves. And it works! :) Sorry for bother.

Related

Do i get malformed input from inputStream coming off a socket?

I am trying to connect to WebSocket (currently using the chrome extension). I got it working that I receive the data. When I try to convert the integers I receive from the input stream I get totally other values. In the example below I gave in the word test as input. So do I do something wrong or do I interpret the input wrong?
Code:
#Override
public void run() {
while (true) {
try {
if(client.getInputStream().read() != -1) {
int i = client.getInputStream().read();
System.out.println("Integer: " + i);
byte b = (byte) i;
System.out.println("Byte: " + b);
char c = (char) b;
System.out.println("Char: " + c);
System.out.println("-=-=-=-=-=-=-=-=-=-=-=-");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output:
output
A java stream returns bytes, but read returns an int so that you can get -1. So if your server is sending an int, then you need to read all of the bytes for that int and create it.
You have two ways to do this. Create a byte[] and use it as a buffer or if you're receiving 32 bit twos complement integers then you can use a DataInputStream.
An example using a buffer.
#Override
public void run() {
while (true) {
try {
byte[] buffer = new byte[4];
if(client.getInputStream().read(buffer) != -1) {
int i = (
( ( buffer[0] & 0xff ) << 24 ) |
( ( buffer[1] & 0xff ) << 16 ) |
( (buffer[2] & 0xff ) << 8) |
(buffer[3] & 0xff)
);
System.out.println("Integer: " + i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Or you can use a DataInputStream.
DataInputStream dis = new DataInputStream(client.getInputStream());
try{
int i = dis.readInt();
} catch( EOFException e){
//end of file. This is like getting -1.
} catch( IOException e ){
//do something for an error
}
The buffer version uses the same conversion as java's DataInput.readInt, I've included it for an example. Also there is some additional checking that should be done. Even though the buffer is 4 bytes long, you could read anything from 1 to 4 bytes.

Client/Server transfer byte array in chunks, heap space error

I need to send a byte array ~500kb between from client to server,and sometimes it gets heap space error (trying to create byte array about 1 or 2gb).'-Xmx' command dont help
byte array structure [size of package]+[chunk of data]
Here's code.
Client:
public class Client_mk1 {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 3030);
Client_mk1 clientMk1=new Client_mk1();
FileInputStream fileInputStream=null;
File file = new File("SOMEFILE");
byte[] data = new byte[(int) file.length()];
System.out.println("file size"+(int) file.length());
fileInputStream = new FileInputStream(file);
fileInputStream.read(data);
fileInputStream.close();
clientMk1.ToChunks(socket,data);
}
void ToChunks (Socket socket, byte[] data) throws IOException {
BufferedOutputStream outputstream = new BufferedOutputStream(socket.getOutputStream());
int chunksize = 50*1024;
int length = data.length;
for (int i = 0; i < length - chunksize + 1 ; i+= chunksize) {
byte[] datasize =IntToByteArray(chunksize);
ByteBuffer byteBuffer = ByteBuffer.allocate(chunksize + datasize.length);
System.out.println("TRY TO SEND "+IntFromByteArray(datasize));
byteBuffer.put(datasize);
byteBuffer.put(Arrays.copyOfRange(data, i, i + chunksize));
outputstream.write(byteBuffer.array());
outputstream.flush();
}
if (length % chunksize !=0){
byte[] datasize = IntToByteArray(length % chunksize);
ByteBuffer bytebuffer = ByteBuffer.allocate((length % chunksize) + datasize.length);
System.out.println("TRY TO SEND "+IntFromByteArray(datasize));
bytebuffer.put(datasize);
bytebuffer.put(Arrays.copyOfRange(data, length - length % chunksize, length));
outputstream.write(bytebuffer.array());
outputstream.flush();
}
}
public static byte[] IntToByteArray(int value) {
return new byte[] {
(byte)(value >> 24),
(byte)(value >> 16),
(byte)(value >> 8),
(byte)value };
}
public static int IntFromByteArray(byte[] bytes) {
return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}
Server:
public class Server_mk1 implements Runnable {
Socket socket;
Server_mk1(Socket socket){
this.socket=socket;
}
public static void main(String[] args) throws IOException {
ServerSocket serversocket = new ServerSocket(3030);
while (true){
Socket socket = serversocket.accept();
Thread dd=new Thread(new Server_mk1(socket));
dd.start();
}
}
public void run() {
BufferedInputStream bufferedInputStream = null;
try {
bufferedInputStream = new BufferedInputStream(this.socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
while (true){
try {
byte[] size=new byte[4];
System.out.println("Reading");
bufferedInputStream.read(size);
System.out.println("SIZE "+Client_mk1.IntFromByteArray(size));
//PROBLEM HERE create to big array for java heap space
byte[] recievedData=new byte[Client_mk1.IntFromByteArray(size)];//PROBLEM create to big array for java heap space
bufferedInputStream.read(recievedData);
System.out.println(new String(recievedData));
}catch (Exception e){
break;
}
}
System.out.println("RUNNING ON _"+Thread.currentThread().getName());
}
What am I doing wrong?
You're doing a lot of things wrong. Minor details include writing socket.getOutputStream() everywhere, which is just ridiculous. Your ByteBuffer code looks way more complex than writing data to a socket needs to be, and for some reason you're using a for loop, probably because you don't really understand how socket communication works.
Your client code can be replaced with the following (sout is the SocketOutputStream).
sout.write(IntToByteArray(data.length));
sout.write(data);
sout.close();
Your server is broken because you're using available() and you don't understand what it does. In short, don't use available(), you'll never need it and it will never help you. The basic idiom for reading data from an InputStream is as follows
byte[] buf = new byte[8192]; // A smallish buffer to read the bytes
int bytesRead = 0; // Track the amount of bytes we actually read
while((bytesRead = in.read(byte) != -1) { // -1 indicates end of stream
// buf now contains bytesRead amount of new bytes to be processed
}
I'm a bit confused because you're working with Sockets and ByteBuffer, yet the rest of your code looks very amateurish.

Audio streaming via TCP socket on Android

I am streaming mic input from a C Server via socket. I know the stream works because it does with a C client and I am getting the right values on my Android client.
I am streaming a 1024 floatarray. One float are 4 bytes. So I got a incoming stream with 4096 bytes per frame. I am getting the floats out of this bytes and I know this floats are the ones I sent, so that part should work.
Now I want to get that stream directly to the phones speakers by using AudioTrack. I tried to input the bytes I received directly: just noise. I tried to cast it back to a byte array, still the same. I tried to cast that float into short (because AudioTrack takes bytes or short). I could get something that could have been my mic input (knocking), but very scratchy and and extremely laggy. I would understand if there was a lag between the frames, but I can't even get one clear sound.
I can, however, output a sin sound clearly that I produce locally and put into that shortarray.
Now I wonder if I got some issues in my code anyone of you can see, because I don't see them.
What I am doing is: I put 4 bytes in a byte array. I get the float out of it. As soon as I got one Frame in my float array (I am controlling that with a bool, not nice, but it should work) I put it in my shortarray and let audiotrack play it. This double casting might be slow, but I do it because its the closest I got to playing the actual input.
Edit:
I checked the endianess by comparing the floats, they have the proper values between -1 and 1 and are the same ones I send. Since I don't change the endianess when casting to float, I don't get why forwarding a 4096 byte array to AudioTrack directly doesn't work neither. There might be something wrong with the multithreading, but I don't see what it could be.
Edit 2: I discovered a minor problem - I reset j at 1023. But that missing float should not have been the problem. What I did other than that was to put the method that took the stream from the socket in another thread instead of calling it in a async task. That made it work, I now am able to understand the mic sounds. Still the quality is very poor - might there be a reason for that in the code? Also I got a delay of about 10 seconds. Only about half a second is caused by WLAN, so I wonder if it might be the codes fault. Any further thoughts are appreciated.
Edit 3: I played around with the code and implemented a few of greenapps ideas in the comments. With the new thread structure I was facing the problem of not getting any sound. Like at all. I don't get how that is even possible, so I switched back. Other things I tried to make the threads more lightweight didn't have any effect. I got a delay and I got a very poor quality (I can identify knocks, but I can't understand voices). I figured something might be wrong with my convertions, so I put the bytes I receive from the socket directly in AudioTrack - nothing but ugly pulsing static noise. Now I am even more confused, since this exact stream still works with the C client. I will report back if I find a solution, but still any help is welcome.
Edit 4 I should add, that I can play mic inputs from another android app where I send that input directly as bytes (I would exclude the float casting stuff and put the bytes I receive directly to audioTrack in my player code).
Also it occured to me, that it could be a problem, that the said floatarray that is streamed by the C Server comes from a 64bit machine while the phone is 32bit. Could that be a problem somehow, even though I am just streaming floats as 4 bytes?
Or, another thought of mine: The underlying number format of the bytes I receive is float. What format does AudioTrack expect? Even if put in just bytes - would I need to cast that float to a int and cast that back to bytes or something?
new code:
public class PCMSocket {
AudioTrack audioTrack;
boolean doStop = false;
int musicLength = 4096;
byte[] music;
Socket socket;
short[] buffer = new short[4096];
float[] fmusic = new float[1024];
WriteToAudio writeThread;
ReadFromSocket readThread;
public PCMSocket()
{
}
public void start()
{
doStop = false;
readThread = new ReadFromSocket();
readThread.start();
}
public class ReadFromSocket extends Thread
{
public void run()
{
doStop=true;
InetSocketAddress address = new InetSocketAddress("xxx.xxx.xxx.x", 8000);
socket = new Socket();
int timeout = 6000;
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
musicLength = 1024;
InputStream is = null;
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
try{
int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT );
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, minSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
} catch (Throwable t)
{
t.printStackTrace();
doStop = true;
}
writeThread = new WriteToAudio();
readThread.start();
int i = 0;
int j=0;
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
music = new byte[4];
while (dis.available() > 0)
{
music[i]=0;
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
fmusic[j]=asFloat;
}
i++;
j++;
if(i==4)
{
music = new byte[4];
i=0;
}
if(j==1024)
{
j=0;
if(doStop)doStop=false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
public class WriteToAudio extends Thread
{
public void run()
{
while(true){
while(!doStop)
{
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
doStop = true;
}
}
}
};
public void writeSamples(float[] samples)
{
fillBuffer( samples );
audioTrack.write( buffer, 0, samples.length );
}
private void fillBuffer( float[] samples )
{
if( buffer.length < samples.length )
buffer = new short[samples.length];
for( int i = 0; i < samples.length; i++ )
{
buffer[i] = (short)(samples[i] * Short.MAX_VALUE);
}
}
}
old code:
public class PCMSocket {
AudioTrack audioTrack;
WriteToAudio thread;
boolean doStop = false;
int musicLength = 4096;
byte[] music;
Socket socket;
short[] buffer = new short[4096];
float[] fmusic = new float[1024];
public PCMSocket()
{
}
public void start()
{
doStop = false;
new GetStream().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private class GetStream extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... values) {
PCMSocket.this.getSocket();
return null;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(Void result)
{
return;
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
private void getSocket()
{
doStop=true;
InetSocketAddress address = new InetSocketAddress("xxx.xxx.xxx.x", 8000);
socket = new Socket();
int timeout = 6000;
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
musicLength = 1024;
InputStream is = null;
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
try{
int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT );
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, minSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
} catch (Throwable t)
{
t.printStackTrace();
doStop = true;
}
thread = new WriteToAudio();
thread.start();
int i = 0;
int j=0;
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
music = new byte[4];
while (dis.available() > 0)
{
music[i]=0;
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
fmusic[j]=asFloat;
}
i++;
j++;
if(i==4)
{
music = new byte[4];
i=0;
}
if(j==1023)
{
j=0;
if(doStop)doStop=false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class WriteToAudio extends Thread
{
public void run()
{
while(true){
while(!doStop)
{
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
doStop = true;
}
}
}
};
public void writeSamples(float[] samples)
{
fillBuffer( samples );
audioTrack.write( buffer, 0, samples.length );
}
private void fillBuffer( float[] samples )
{
if( buffer.length < samples.length )
buffer = new short[samples.length*4];
for( int i = 0; i < samples.length; i++ )
{
buffer[i] = (short)(samples[i] * Short.MAX_VALUE);
}
}
}
Sooo...I just solved this only hours after I desperatly put bounty on it, but thats worth it.
I decided to start over. For the design thing with threads etc. I took some help from this awesome project, it helped me a lot. Now I use only one thread. It seems like the main point was the casting stuff, but I am not too sure, it also may have been the multithreading. I don't know what kind of bytes the byte[] constructor of AudioTracker expects, but certainly no float bytes. So I knew I need to use the short[] constructor. What I did was
-put the bytes in a byte[]
-take 4 of them and cast them to a float in a loop
-take each float and cast them to shorts
Since I already did that before, I am not too sure what the problem was. But now it works.
I hope this can help someone who wents trough the same pain as me. Big thanks to all of you who participated and commented.
Edit: I just thought about the changes and figured that me using CHANNEL_CONFIGURATION_STEREO instead of MONO earlier has contributed a lot to the stuttering. So you might want to try that one first if you encounter this problem. Still for me it was only a part of the solution, changing just that didn't help.
static final int frequency = 44100;
static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
boolean isPlaying;
int playBufSize;
Socket socket;
AudioTrack audioTrack;
playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);
new Thread() {
byte[] buffer = new byte[4096];
public void run() {
try {
socket = new Socket(ip, port);
}
catch (Exception e) {
e.printStackTrace();
}
audioTrack.play();
isPlaying = true;
while (isPlaying) {
int readSize = 0;
try { readSize = socket.getInputStream().read(buffer); }
catch (Exception e) {
e.printStackTrace();
}
short[] sbuffer = new short[1024];
for(int i = 0; i < buffer.length; i++)
{
int asInt = 0;
asInt = ((buffer[i] & 0xFF) << 0)
| ((buffer[i+1] & 0xFF) << 8)
| ((buffer[i+2] & 0xFF) << 16)
| ((buffer[i+3] & 0xFF) << 24);
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
int k=0;
try{k = i/4;}catch(Exception e){}
sbuffer[k] = (short)(asFloat * Short.MAX_VALUE);
i=i+3;
}
audioTrack.write(sbuffer, 0, sbuffer.length);
}
audioTrack.stop();
try { socket.close(); }
catch (Exception e) { e.printStackTrace(); }
}
}.start();
Get rid of all, all, the available() tests. Just let your code block in the following read() statement(s). You don't have anything better to do anyway, and you're just burning potentially valuable CPU cycles by even trying to avoid the block.
EDIT To be specific:
try {
socket.connect(address, timeout);
} catch (IOException e2) {
e2.printStackTrace();
}
Poor practice to catch this exception and allow the following code to continue as though it hadn't happened. The exception should be allowed to propagate to the caller.
try {
is = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
Ditto.
try {
if(dis.available()>0)Log.d("PCMSocket", "receiving");
Remove. You're receiving anyway.
music = new byte[4];
while (dis.available() > 0)
Pointless. Remove. The following reads will block.
{
music[i]=0;
Pointless. Remove.
music[i] = dis.readByte();
if(i==3)
{
int asInt = 0;
asInt = ((music[0] & 0xFF) << 0)
| ((music[1] & 0xFF) << 8)
| ((music[2] & 0xFF) << 16)
| ((music[3] & 0xFF) << 24);
This is all pointless. Replace it all with short asInt = dis.readInt();.
float asFloat = 0;
asFloat = Float.intBitsToFloat(asInt);
Given that the original conversion to short was via floatValue * Short.MAX_VALUE, this conversion should be asFloat = (float)asInt/Short.MAX_VALUE.
if(i==4)
If i was 3 before it will be 4 now, so this test is also pointless.
music = new byte[4];
You don't need to reallocate music. Remove.
} catch (IOException e) {
e.printStackTrace();
}
See above. Pointless. The exception should be allowed to propagate to the caller.
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
All this should be in a finally block.
}
};
while(true){
while(!doStop)
You don't need both these loops.
try{
writeSamples(fmusic);
}catch(Exception e)
{
e.printStackTrace();
}
See above. Pointless. The exception should in this case terminate the loop, as any IOException writing to a socket is fatal to the connection.
if( buffer.length < samples.length )
buffer = new short[samples.length];
Why isn't buffer already the right size? Alternatively, what if buffer.length > samples.length?

Java serial port write/send ASCII data

My problem is that I need to control mobile robot E-puck via Bluetooth in Java, by sending it commands like "D,100,100" to set speed, "E" to get speed, and etc. I have some code:
String command = "D,100,100";
OutputStream mOutputToPort = serialPort.getOutputStream();
mOutputToPort.write(command.getBytes());
So with this method write I can only send byte[] data, but my robot won't understand that.
For example previously I have been using this commands on Matlab like that:
s = serial('COM45');
fopen(s);
fprintf(s,'D,100,100','async');
Or on program Putty type only:
D,100,100 `enter`
Additional info:
I've also figured out, that Matlab has another solution for same thing.
s = serial('COM45');
fopen(s);
data=[typecast(int8('-D'),'int8') typecast(int16(500),'int8') typecast(int16(500),'int8')];
In this case:
data = [ -68 -12 1 -12 1];
fwrite(s,data,'int8','async');
Wouldn't it be the same in Java:
byte data[] = new byte[5];
data[0] = -'D';
data[1] = (byte)(500 & 0xFF);
data[2] = (byte)(500 >> 8);
data[3] = (byte)(500 & 0xFF);
data[4] = (byte)(500>> 8);
And then:
OutputStream mOutputToPort = serialPort.getOutputStream();
mOutputToPort.write(data);
mOutputToPort.flush();
Main details in code comments. Now you can change wheel speed by typing in command window D,1000,-500 and hitting enter.
public class serialRobot {
public static void main(String[] s) {
SerialPort serialPort = null;
try {
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier("COM71");
if (portIdentifier.isCurrentlyOwned()) {
System.out.println("Port in use!");
} else {
System.out.println(portIdentifier.getName());
serialPort = (SerialPort) portIdentifier.open(
"ListPortClass", 300);
int b = serialPort.getBaudRate();
System.out.println(Integer.toString(b));
serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.setInputBufferSize(65536);
serialPort.setOutputBufferSize(4096);
System.out.println("Opened " + portIdentifier.getName());
OutputStream mOutputToPort = serialPort.getOutputStream();
InputStream mInputFromPort = serialPort.getInputStream();
PerpetualThread t = readAndPrint(mInputFromPort);
inputAndSend(mOutputToPort);
t.stopRunning();
mOutputToPort.close();
mInputFromPort.close();
}
} catch (IOException ex) {
System.out.println("IOException : " + ex.getMessage());
} catch (UnsupportedCommOperationException ex) {
System.out.println("UnsupportedCommOperationException : " + ex.getMessage());
} catch (NoSuchPortException ex) {
System.out.println("NoSuchPortException : " + ex.getMessage());
} catch (PortInUseException ex) {
System.out.println("PortInUseException : " + ex.getMessage());
} finally {
if(serialPort != null) {
serialPort.close();
}
}
}
private static PerpetualThread readAndPrint(InputStream in) {
final BufferedInputStream b = new BufferedInputStream(in);
PerpetualThread thread = new PerpetualThread() {
#Override
public void run() {
byte[] data = new byte[16];
int len = 0;
for(;isRunning();) {
try {
len = b.read(data);
} catch (IOException e) {
e.printStackTrace();
}
if(len > 0) {
System.out.print(new String(data, 0, len));
}
}
}
};
thread.start();
return thread;
}
private static void inputAndSend(OutputStream out) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int k = 0;
for(;;) {
String komanda;
try {
komanda = in.readLine();
} catch (IOException e) {
e.printStackTrace();
return;
}
komanda = komanda.trim();
if(komanda.equalsIgnoreCase("end")) return;
byte komandaSiust[] = proces(komanda); //Command we send after first
//connection, it's byte array where 0 member is the letter that describes type of command, next two members
// is about left wheel speed, and the last two - right wheel speed.
try {
if(k == 0){
String siunc = "P,0,0\n"; // This command must be sent first time, when robot is connected, otherwise other commands won't work
ByteBuffer bb = ByteBuffer.wrap(siunc.getBytes("UTF-8"));
bb.order(ByteOrder.LITTLE_ENDIAN);
out.write(bb.array());
out.flush();
}else{
ByteBuffer bb = ByteBuffer.wrap(komandaSiust);
bb.order(ByteOrder.LITTLE_ENDIAN);
out.write(bb.array());
out.flush();
}
k++;
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
private static byte[] proces(String tekstas){
tekstas = tekstas.trim();
char[] charArray = tekstas.toCharArray();
byte kodas1[];
int fComa = tekstas.indexOf(',', 1);
int sComa = tekstas.indexOf(',', 2);
int matavimas = charArray.length;
int skir1 = sComa - fComa - 1;
int skir2 = matavimas - sComa -1;
char leftSpeed[] = new char[skir1];
for(int i = 0; i < skir1; i++){
leftSpeed[i] = charArray[fComa + i + 1];
}
char rightSpeed[] = new char[skir2];
for(int i = 0; i < skir2; i++){
rightSpeed[i] = charArray[sComa + i + 1];
}
String right = String.valueOf(rightSpeed);
String left = String.valueOf(leftSpeed);
int val1 = Integer.parseInt(left);
int val2 = Integer.parseInt(right);
kodas1 = new byte[5];
kodas1[0] = (byte)-charArray[0];
kodas1[1] = (byte)(val1 & 0xFF);
kodas1[2] = (byte)(val1 >> 8);
kodas1[3] = (byte)(val2 & 0xFF);
kodas1[4] = (byte)(val2 >> 8);
return kodas1;
}
private static class PerpetualThread extends Thread {
private boolean isRunning = true;
public boolean isRunning() { return isRunning; }
public void stopRunning() {
isRunning = false;
this.interrupt();
}
}
}
According to the documentation, you need to call setSerialPortParams(int baudrate, int dataBits, int stopBits, int parity) on your serial port.

Speed up my Java tcp transfer!

I need to speed up transfers across my gigabit ethernet connection. Right now, I'm doing something almost exactly like this, but I'm only seeing about 40% of that when I run this code below.
I also ran this script on all of my (Mac Pro) machines before testing
#!/bin/bash
sudo sysctl -w net.inet.tcp.win_scale_factor=8
sudo sysctl -w kern.ipc.maxsockbuf=16777216
sudo sysctl -w net.inet.tcp.sendspace=8388608
sudo sysctl -w net.inet.tcp.recvspace=8388608
The actual code follows:
import java.io.*;
import java.nio.*;
import java.net.*;
public class BandwidthTester {
private static final int OUT_BUF = (1 << 17),
IN_BUF = (1 << 17), SEND_BUF = (1 << 22), RECV_BUF = (1 << 22);
public static void main(String[] args) {
try {
// server
if (args.length == 0) {
ServerSocket sock = new ServerSocket();
sock.bind(new InetSocketAddress(41887));
// wait for connection
Socket s = sock.accept();
s.setSendBufferSize(SEND_BUF);
System.out.println("Buffers: " + s.getSendBufferSize() + " and " + s.getReceiveBufferSize());
sock.close();
BufferedOutputStream bOut = new BufferedOutputStream(s.getOutputStream(), OUT_BUF);
// send lots of data
sendLotsOfData(bOut);
} else if (args.length == 2) {
String host = args[0];
int port = Integer.parseInt(args[1]);
System.out.println("Connecting to " + args[0] + ":" + args[1]);
Socket sock = new Socket();
sock.setReceiveBufferSize(RECV_BUF);
sock.connect(new InetSocketAddress(host, port));
System.out.println("Buffers: " + sock.getSendBufferSize() + " and " + sock.getReceiveBufferSize());
BufferedInputStream bIn = new BufferedInputStream(sock.getInputStream(), IN_BUF);
getLotsOfData(bIn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getLotsOfData(InputStream in) {
System.out.println("Getting data...");
try {
long start = System.currentTimeMillis();
ByteBuffer intConv = ByteBuffer.allocate(4);
in.read(intConv.array());
int len = intConv.getInt(0);
for (int i=0; i < len; i++) {
in.read(intConv.array());
int val = intConv.getInt(0);
}
long end = System.currentTimeMillis();
double elapsed = ((double)(end - start)) / (1000.0);
System.out.println("Read in " + elapsed + " seconds: " + ( (4.0*8.0*len/elapsed) + " bits per second"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void sendLotsOfData(OutputStream out) {
System.out.println("Sending data...");
try {
long start = System.currentTimeMillis();
int len = (1 << 29);
ByteBuffer intConv = ByteBuffer.allocate(4);
intConv.putInt(0, len);
out.write(intConv.array());
for (int i=0; i < len; i++) {
intConv.putInt(0, i);
out.write(intConv.array());
}
out.flush();
long end = System.currentTimeMillis();
double elapsed = ((double)(end - start)) / (1000.0);
System.out.println("Sent in " + elapsed + " seconds: " + ( (4.0*8.0*len/elapsed) + " bits per second"));
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Any suggestions? It's taking about 42 seconds to send all of that data, but even a 10% improvement here would have a dramatic impact on my program.
One thing you might try is using a larger buffer for the ByteBuffer. Going from 4 bytes to 16, I went from a 12 second transfer time to a 9 second transfer time. (tested using 2^26 rather then 2^29 for length)
That said, it was being run locally; so no actual network issues should have been encountered.
Somewhat dirty modified code for sending:
ByteBuffer intConv = ByteBuffer.allocate(16);
intConv.putInt(0, len);
out.write(intConv.array(),0,4);
for (int i=0; i < len; i+=4) {
for(int j=0; j<4; j++)
intConv.putInt(4*j, i);
out.write(intConv.array());
}
And Receiving:
ByteBuffer intConv = ByteBuffer.allocate(16);
in.read(intConv.array(),0,4);
int len = intConv.getInt(0);
for (int i=0; i < len; i+=4) {
in.read(intConv.array());
for(int j=0; j<4; j++)
{
int val=intConv.getInt(j*4);
}
}
Clearly the receiving end would need some modification to handle strange and odd cases like 'what if there were only 3 ints remaining/read from the stream', but I think this would be enough to see if it improves performance.

Categories