I'm working on implementing a RCON for Minecraft, which uses the Valve rcon protocol, and I've gotten my hands on the C source and tried to implement it into Java, this is what I've done so far:
Creating the packet:
http://pastebin.com/9AeiSQPD
Recieve the packet: http://pastebin.com/n6V1KnPa
Send the packet: http://pastebin.com/rixhD15p
I'm sending the AUTH packet to the server and trying to receive a response, but the return value is null, also trying to send a command throws:
Software caused connection abort: socket write error
What am I doing wrong?
I think there are two wrong things in your code.
1) First as you can see here the packet structure use 4 bytes blocks (32bits) of little-endian integer. The means reverse blocks (see here).
2) Second you didnt use null block (empty String or null character) at end of packet.
Solution:
1) Use: (ByteBuffer and ByteOrder are from native java.nio java7)
writer.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
.putInt(p.size).array());
writer.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
.putInt(p.id).array());
writer.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
.putInt(p.cmd).array());
Instead of:
writer.writeInt(p.size);
writer.writeInt(p.id);
writer.writeInt(p.cmd);
and:
ByteBuffer.wrap(<4_BLOCKS_BYTES>)
.order(ByteOrder.LITTLE_ENDIAN).getInt();
instead of:
reader.readInt();
where <4_BLOCKS_BYTES> is a bytes array of size 4, read from the reader.
And if I can give you an advice, it can be easier (I think) to use a global buffer to send.
I mean a buffer containing size, id, type, data and empty block as bytes. And same thing when you read response: use a buffer to read while the DataInputStream is available() and then parse it.
Good luck!
Java Doc links:
java.nio: docs.oracle.com/javase/7/docs/api/java/nio/package-frame.html
DataInputStream.available(): docs.oracle.com/javase/7/docs/api/java/io/FilterInputStream.html#available()
Related
I'm trying to create application that will send object through local network using Sockets. When i run server and client code in Intellij Idea they work fine, but when i run server code on one pc and client code on another pc i get errors like java.io.StreamCorruptedException: invalid type code: 00 or java.io.StreamCorruptedException: invalid stream header: 6C69656E
byte[] readBuffer = new byte[4096];
int num = inStream.read(readBuffer); //inStream is socket input stream
ByteArrayInputStream bis = new ByteArrayInputStream(readBuffer);
ObjectInput in = new ObjectInputStream(bis);
Object o = in.readObject(); //this line throws error
The thing is that writing and reading object to socket stream works on server (which is on pc where i created project) but reading from input stream on client (another pc where i copied project) throws error.
Can someone help me with this? I searched everywhere for solution but i can't figure out what is problem with serializing, because it works on same pc but won't on another. Is there any way that i can make this pc independent? This also happens when i create jar files and run it on same pc where it works in Intellij Idea.
It can because that client didnt read message fully.
But the real mistake is that you work with TCP socket like a message protocol transport but TCP is a stream protocol so you have to create your own message protocol on top of TCP.
Why it works fine on local system?
Because transport data between client and server happen too fast in local test and maybe in just one frame so all the message transported in just one IO-call but in internet or a network it doesn't work like you think.
There is 2 way to handle this mistake:
1- Pass SocketInputStream directly to ObjectInputStream instance and let it handle read objects.
2- Create a message protocol for example you can put the size of message in 2 or more first bytes. Then you can workd like this :
Read 2(or more) first bytes and detect size of packet.
Create a buffer for this size and read packet bytes.(make sure you read all of packet data from socket . You can use return value of SocketInputStream.read(byte[]) method to calculate it)
Pass the packet to ObjectInputStream and read object !
I've searched high and low and havn't been able to find an answer to this question. I'm fairly certain that it isn't difficult, probably a case of my searches not using the right verbiage.
I'm working on communicating with a gimbal controller. I've been able to connect to the serial port that it's connected to. I'm unable to find information on how to format a message like this:
Each command consists
of the header and the body, both with checksum. Commands with the wrong header or body checksum, or
with the body size that differs from expected, should be ignored.
Can anyone point me in the direction on how to write to the serial port like that?
Thank you,
Loren
There are two typical approaches for communicating with a device over a serial port. In both cases, the end result is raw bits being sent over the wire. You can send ASCII strings if your device expects it but since your excerpt specifically mentioned packet packaging, I would venture to say that they want bytes.
The excerpt you copy pasted sounds like it came from a manual that explains the exact protocol that is required to communicate. In a nutshell, you will be doing the following.
Setup the serial port
Prepare your serial data
Send your serial data
I like to use jSSC for serial comms though lots of other folks use RXTX. jSSC has been more reliable for me so that's what I'll be using in my example. The manual for your device should specify the required baud rate, data bits, stop bits, parity, and handshake (if any).
Here we setup a port (replace your parameters as needed). See the docs on the details of this. https://github.com/scream3r/java-simple-serial-connector
SerialPort _port = new SerialPort(portName);
_port .openPort();
_port.setParams(baudRate, dataBits, stopBits, parity, setRTS, setDTR);
_port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
To construct your data packet, you will need to know:
Header format
Length requirement/restrictions
Checksum method
Here I am going to make a super simple packet that is just an example and is most likely not applicable to your use case.
// Format is: [length][7 data bytes][8 bit additive checksum]
// Create an empty byte array
byte[] packet = new byte[8];
// Our simple header
packet[0] = packet.length;
// Some data
byte[] dummyData = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
// Copy data to packet
System.arraycopy(dummyData, 0, packet, 0, dummyData.length);
// Go do the checksum (good exercise for you)
byte checksum = getChecksum(packet);
packet[7] = checksum;
So now we have a port, some data... and now what? Let's send it.
// Just send those bytes
_port.writeBytes(packet);
Once you get the hang of things, read into how you read the response, setup OnDataReceivedEvents, and how to more efficiently create packets. Some good terms to Google:
jSSC
Java RXTX
Baud Rate
Async/Sync data handling
In my server-side code I need to be able to listen to a socket to exchange JSON 'packets' with a Java 7 test application on the same machine. The connection is made and a JSON string is constructed and written to the socket by the Java test application. It is received by the Dart server-side application and passed to a callback method, handleJson, which attempts to decode it. The process dies on 'JSON.decode'.
I think it dies because the string is prepended, by the Java 'writeUTF' method with a short int that contains the number of bytes in the JSON UTF-8 uncluding the leading short and the leading byte is 0.
Is there a Dart method to handle this, in each direction, or must I write the code? (I had thought that JSON work easily between languages.)
The JSON string before writing to the socket in my Java test application:
{"target":"DOOR","command":"OPEN"} // 34 characters
A Java snippet:
// in a try-catch
Socket client = new Socket(serverName, port);
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF(json);
client.close();
The Java documentation states that the out.writeUTF method converts the json string to UTF-8 with the string length prepended as a short int containing the total number of bytes written.
In main:
ServerSocket.bind('127.0.0.1', 4041)
.then((serverSocket) {
print('connected');
// prints: 'connected'
serverSocket.listen((socket) {
socket.transform(UTF8.decoder).listen(handleJson);
});
});
handleJson method:
handleJson(String stringAsJson){
print('string length is ' + (stringAsJson.length).toString());
// prints: 'string length is 36'
print('received json $stringAsJson');
// prints: 'received json '
String json = JSON.decode(stringAsJson);
// dies on decode
print('Sever Socket received: $json');
}
This will give you some troubles, since Socket is raw TCP, and TCP is streaming. That means that the text (bytes) you send can be split and merged in any way the network may find suitable.
In your case, you need a way to mark the end of each JSON message. An example could be to accumulate all bytes received, until the byte 0 is seen (invalid in JSON). Those bytes could then be converted to UTF8 and then again converted to JSON. Note that the peer needs to send this 0 byte in between messages, for this to work.
Now, you also consider using WebSockets as a way to sent messages. After the initial HTTP handshake, it's actually just a raw TCP socket with some extra header information, to make it package oriented - exactly what you need. dart:io already includes a WebSocket implementation.
My problem is that C sockets look to act differently than Java sockets. I have a C proxy and I tested it between a workload generator (oltp benchmark client written in Java) and the JDBC connector of the Postgres DB.
This works great and forwards data from one to other, as it should. We need to make this proxy work in Java, so I used plain ServerSocket and Socket classes from java.net and I cannot make it work. The Postgres returns an authentication error message, assuming that the client did not send the correct password.
Here is how the authentication at the JDBC protocol works:
-client sends a requests to connect to a database specifying the database name and the username
-server responds back with a one time challenge message (13 byte message with random content)
-client concatenates this message with the user password and performs a md5 hash
-server compares the hash got from the client with the hash he computes
[This procedure is performed in order to avoid replay attacks (if client would send only the md5 hash of its password then an attacker could replay this message, pretending he is the client)]
So I inspected the packets with tcpdump and they look correct! The size is exactly as it should, so maybe the content is corrupted (??)
Sometimes though the DB server responds ok for the authentication (depending on the value of the challenge message)!! And then the oltp client sends a couple of queries, but it crashes in a while…
I guess that maybe it has to do with the encoding, so I tried with the encoding that C uses (US-ANSII), but still the same.
I send the data using fixed size character or byte arrays both in C and in Java!
I really don't have any more ideas, as I tried so many cases...
What is your guess of what would be the problem?
Here is a representative code that may help you have a more clear view:
byte [] msgBuf;
char [] msgBufChars;
while(fromInputReader.ready()){
msgBuf = new byte[1024];
msgBufChars = new char[1024];
// read data from one party
int read = fromInputReader.read(msgBufChars, 0, 1024);
System.out.println("Read returned : " + read);
for(int i=0; i<1024; i++)
msgBuf[i] = (byte) msgBufChars[i];
String messageRead = new String(msgBufChars);
String messageToWrite = new String(msgBuf);
System.out.println("message read : "+messageRead);
System.out.println("message to write : "+new String(messageToWrite));
// immediatelly write data to other party (write the amount of data we read (read value) )
// there is no write method that takes a char [] as a parameter, so pass a byte []
toDataOutputStream.write(msgBuf, 0, read);
toDataOutputStream.flush();
}
There are a couple of message exchanges in the beginning and then Postgres responds with an authentication failure message.
Thanks for your time!
What is your guess of what would be the problem?
It is nothing to do with C versus Java sockets. It is everything to do with bad Java code.
I can see some problems:
You are using a Reader in what should be a binary stream. This is going to result in the data being converted from bytes (from the JDBC client) to characters and then back to bytes. Depending on the character set used by the reader, this is likely to be destructive.
You should use plain, unadorned1 input streams for both reading and writing, and you should read / write to / from a preallocated byte[].
This is terrible:
for(int i=0; i<1024; i++)
msgBuf[i] = (byte) msgBufChars[i];
If the characters you read are not in the range 0 ... 255 you are mangling them when you stuff them into msgBuf.
You are assuming that you actually got 1024 characters.
You are using the ready() method to decide when to stop reading stuff. This is almost certainly wrong. Read the javadoc for that method (and think about it) and you should understand why it is wrong. (Hint: what happens if the proxy can read faster than the client can deliver?)
You should use a while(true), and then break out of the loop if read tells you it has reached the end of stream; i.e. if it returns -1 ...
1 - Just use the stream objects that the Socket API provides. DataXxxStream is unnecessary because the read and write methods are simply call-throughs. I wouldn't even use BufferedXxxStream wrappers in this case, because you are already doing your own buffering using the byte array.
Here's how I'd write that code:
byte [] buffer = new byte[1024]; // or bigger
while(true) {
int nosRead = inputStream.read(buffer);
if (nosRead < 0) {
break;
}
// Note that this is a bit dodgy, given that the data you are converting is
// binary. However, if the purpose is to see what embedded character data
// looks like, and if the proxy's charset matches the text charset used by
// the client-side JDBC driver for encoding data, this should achieve that.
System.out.println("Read returned : " + nosRead);
System.out.println("message read : " + new String(buffer, 0, nosRead));
outputStream.write(buffer, 0, nosRead);
outputStream.flush();
}
C sockets look to act differently than Java sockets.
Impossible. Java sockets are just a very thin layer over C sockets. You're on the wrong track with this line of thinking.
byte [] msgBuf;
char [] msgBufChars;
Why are you reading chars when you want to write bytes? Don't use Readers unless you know that the input is text.
And don't call ready(). There are very few correct uses, and this isn't one of them. Just block.
I just want to create echo server/client using protobuf and java.
I tested with protobuf-java-2.4.1 and jdk1.7.
I wrote echo server code like below
// create server socket and accept for client connection.
// ...
link = servSock.accept();
Person person = Person.parseFrom(link.getInputStream()); // blocking position
person.writeTo(link.getOutputStream());
I think it is not necessary to note Person.proto.
The client code is only send Person object using socket input stream and receive echo Person object.
// socket connect code was omitted.
Person person = Person.newBuilder().setId(1).setName("zotiger").build();
person.writeTo(echoSocket.getOutputStream());
person = Person.parseFrom(echoSocket.getInputStream());
But server was blocked in parseFrom function when the server and client both run.
I found if i use writeDelimitedTo() and parseDelimitedFrom(), then that is ok. I don't understand why the writeTo() and parseFrom() function does not working.
Why did the server blocking in there?
Is it necessary to send some end signal from client side?
The reason you have to use writeDelimitedTo()/parseDelimitedFrom() is that otherwise protocol buffers may have no idea how much data it needs to read from the socket. That presents a problem (I say may because you could of course create a message with only fixed length fields that wouldn't require this ... but protocol buffers has to deal with both cases)
The writeDelimitedTo() method writes the length of the message to the OutputStream then the message itself. Its counterpart parseDelimitedFrom() reads the length, then the message.
You can use writeTo() and pasrseFrom() with streams but only if you want to write/read a single message and are closing the stream after writing. The reader will then get an EOF to indicate the end of the message (also the case when reading from a file that contains only a single message).
Don't write your own Client/Server, ie. RPC solution. There is one here......https://code.google.com/p/protobuf-rpc-pro/ which has some nice features already for java.