Trying to send a byte[] through TCP Socket in Java, it sends a specific array as two separate messages, while other arrays are sending as one message.
In more details, I convert a hex String to a byte[] using the following function:
public static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
And then I send it through Java TCP Socket:
ServerSocket server = new ServerSocket(3030);
Socket socket = server.accept();
OutputStream out = socket.getOutputStream();
// sample hex string
String msg = "F0700F8000F42400001544952414E00000000000000000000000662000E00060000";
out.write(HexUtiles.hexStringToByteArray(msg));
In debugging the code I found out it separate the array in byte number 1024. Also, increasing and decreasing the socket buffer size made no differences.
In addition, there is no 0A (Hex String of \n) in the message! I guess there is some strange behavior in the write method that sends a byte[] array as two messages! How can I send the byte[] as only one message?
There are no "messages" in TCP. Everything is sent as a stream and it's undefined how it will be chunked on the other end. Chunks might be split or joined together.
Send the byte size before each array using DataOutputStream. Read the size on the other end using DataInputStream and then that many bytes. This way you will be able to recreate your arrays at the other end.
Another option is to use ObjectOutputStream and ObjectInputStream and send the arrays as objects.
Related
I am reading TCP packets that I use to display an image. Each packet is supposed to have a 1025 length. And each time, I get 128 lines, I draw an image.
SO I begin by initializing
s = new Socket(ip, port);
stream = s.getInputStream();
byte[] tmpBuffer = new byte[1025];
byte[] finalBuffer = new byte[128 * 1025];
int count_lines =0
and then I read stream by stream of length 1025,
while((read = stream.read(tmpBuffer, 0, tmpBuffer.length)) != -1){
System.arraycopy(tmpBuffer, 1, finalBuffer, count_lines * 1025, 1025);
count_lines++;
if(count_lines == 128) break;
}
The problem is when I log the read integer, I get a bunch of 1025 but sometimes (apparently randomly) 423 or 602 (noticing that 423+602=1025)
Am I going wrong with the TCP reading or is there a problem on the server side ?
Am I going wrong with the TCP reading or is there a problem on the server side ?
In TCP you have only a stream of bytes. You have messages nor control over packets which are typically no more than 1532 bytes long.
You have to have your own protocol to handle sending of messages.
inputStream.read(buffer) will read between 1 byte and buffer.length and you can't ignore the actual length read as you are doing.
If you want to read into a buffer of a known length, you can use
DataInputStream dis = new DataInputStream(socket.getInputStream());
byte[] finalBuffer = new byte[128 * 1025];
dis.readFully(finalBuffer);
The readFully reads as much as it can at a time until the byte[] has been fully read.
A more flexible protocol is to send the length first.
public static void write(DataOutputStream out, byte[] bytes) {
out.writeInt(bytes.length());
out.write(bytes);
}
public static byte[] read(DataInputStream in) {
int length = in.readInt();
byte[] bytes = new byte[length];
in.readFully(bytes);
return bytes;
}
This way, whatever length of data you send, you will recieve in the same manner.
I have a Java application that is reading data from a TCP socket that is receiving XML of varying size. The first 5 bytes of a given packet are supposed to indicate the size of the remaining message. I can read the message and xml successfully if I manually create a large byte[] and read the data.
Here are the instructions from the manual for the application that is generating the data:
Each message is preceded by the message size indicator which is a
32-bit unsinged integer using the network bytes order method. For
example: \x05\x00\x00\x00\x30\x31\x30\x32\x00 indicates the message
size of an ack which is 5 bytes included the fifth message byte '\0'. The
size indicator specifies everything following the size indicator
itself.
However I can't figure out how to decode the first 5 bytes to an integer that I can use to correctly size a byte[] for reading the rest of the message. I get random results:
Here is the code I'm using to parse the message:
DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
BufferedInputStream inFromServer = new BufferedInputStream(clientSocket.getInputStream());
byte[] data = new byte[10];
inFromServer.read(data);
String result = new String(data, "ISO-8859-1");
Logger.info(data+"");
//PROBLEM AREA: Tried reading different byte lengths but no joy
//This should be a number but it never is. Often strange symbols
byte[] numeric = Arrays.copyOfRange(data,1,5);
String numericString = new String(numeric, "ISO-8859-1");
//Create a huge array to make sure everything gets captured.
//Want to use the parsed value from the start here
byte[] message = new byte[1000000];
inFromServer.read(message);
//This works as expected and returns correctly formatted XML
String fullMessage = new String(message, "ISO-8859-1");
Logger.info("Result "+result+ " Full message "+fullMessage);
The length looks like it's little endian. You can still use DataInputStream but you have to swap the bytes. If you used NIO's SocketChannel and a ByteBuffer you could set the byte order, but this is likely to be harder to use.
// only do this once per socket.
DataInputStream in = new DataInputStream(
new BufferedInputStream(clientSocket.getInputStream()));
// for each message.
int len0 = in.readInt();
int len = Integer.reverseBytes(len0);
assert len < 1 << 24;
byte[] bytes = new byte[len];
in.readFully(bytes);
String text = new String(bytes, "ISO-8859-1").trim();
int number = Integer.parseInt(text);
Network byte order is aka big-endian. But seeing your data it seems, that actually little-endian is used. At least 5 will look like those first 4 bytes in little-endian, but not in big-endian. So you need to read those bytes, consider little-endian and convert to long to consider "unsigned-ness".
public static void main(String[] args) throws IOException {
DataInputStream inFromServer = new DataInputStream(new BufferedInputStream(null));
int iSize = inFromServer.readInt();
iSize = Integer.reverseBytes(iSize); //read as little-endian
long count = Integer.toUnsignedLong(iSize); //unsigned int
}
I've been playing around with transferring data between a test client (written in Java) and a server (written in C#/.NET).
I tried TCP clients and servers, but there has been and current is a problem flushing the stream. I realize flush doesn't always flush the stream, so I'm wondering if there is any way to flush/send a stream without .flush() or in a more reliable way?
Currently, the important part of the client looks like this (message is a string, serverSocket is a Socket object):
OutputStream output = serverSocket.getOutputStream();
byte[] buffer = message.getBytes();
int length = buffer.length;
output.write(ByteBuffer.allocate(4).putInt(length).array());
output.write(buffer);
output.flush();
and the server looks like this:
NetworkStream stream = client.GetStream ();
byte[] sizeBuffer = new byte[4];
int read = stream.Read (sizeBuffer, 0, 4);
int size = BitConverter.ToInt32 (sizeBuffer, 0);
Databaser.log ("recieved byte message denoting size: " + size);
byte[] messageBuffer = new byte[size];
read = stream.Read (messageBuffer, 0, size);
string result = BitConverter.ToString (messageBuffer);
Databaser.log ("\tmessage is as follows: '" + result + "'");
Where, if it's not evident from the code, the client sends 4 bytes, which are combined into a 32 bit integer which is the length of the message. Then I read in the message based on that length and have build in converters translate it into a string.
As I said, I'm wondering how to flush the connection? I know this code isn't perfect, but I can change it back to when I used TCP and UTF exclusive string messaging over the network, but either way, the connection doesn't send anything from the client until the client shuts down or closes the connection.
Maybe the problem is in the byte order. I have an application which send from a tablet (java) to a C# application (Windows Intel), I used similar to what you've done, except in the following
ByteBuffer iLength = ByteBuffer.allocate(4);
iLength.order(ByteOrder.LITTLE_ENDIAN);
iLength.putInt(length);
output.write(iLength.array(), 0, 4);
output.write(buffer);
output.flush();
Java uses BIG-ENDIAN and Intel uses LITTLE-ENDIAN bytes order.
I'm developing a server-client application.
The server is done in Java (PC) and the client in Java. (Android)
I'm having trouble with the following implementation:
Server grabs bitmap -> raw bytes -> TCP -> Client (Async Streams
Now the byte array is delivered in multiple packets of different lengths in the client. So to handle this properly, I should use the prefix method.
To use prefix mode you need to send the length of the message in bytes
as four bytes and then the message
My code
public void sendScreenshot(byte[] buffer) throws IOException {
OutputStream os = socket.getOutputStream();
os.write(buffer.length + 1);
os.write((byte) 0);
os.write(buffer, 0, buffer.length);
os.flush();
}
In VB.net, this is achieved in the following code:
Private Sub dat(ByVal dat As String)
Dim nstream As NetworkStream = sock.GetStream()
Dim bit As Byte() = System.Text.Encoding.UTF8.GetBytes(dat)
Dim bw As New BinaryWriter(sock.GetStream())
bw.Write(bit.Length + 1)
bw.Write((byte)command)
bw.Write(bit, 0, bit.length)
End Sub
Any help implementing it in Java is welcome?
Use a DataOutputStream:
DataOutputStream out = new DataOutputStream(os);
out.writeInt(buffer.length + 1);
// This writes a single byte
out.write(0);
out.write(buffer);
out.flush();
The .writeInt() here comes from this part of the text you quoted:
you need to send the length of the message in bytes as four bytes
which means an int. Note that this will write the int in network order. While this is unspecified in your extract, I suppose this is what is expected.
Similarly, on the receiving end, you can use a DataInputStream, read the length as an int and then the payload.
I have this TCP server running Because UDP server was able to accept large packets but ISP blocks my UDP packets even i have Public IP and Static services. But now decided to change it to TCP but i have a large stack now to replace with UDP to TCP.
Here is the server which is running but it does not at once receive a large packet, how can i increase it to unlimited or maximum size or etc?
1) Is there any way with this?
public void run()
{
while(true)
{
byte[] buf=new byte[5024]; <<< not helping!!
int bytes_read = 0;
try {
bytes_read = sockInput.read(buf, 0, buf.length);
String data = null;
data = new String(buf, 0, bytes_read);
System.out.println("[TCP]: incomeing data: " + bytes_read + " bytes, data=" +data);
}
}
2) Is there any way with this?
public TCPHandler(Socket sock) throws IOException
{
sock.setReceiveBufferSize(10*1024 +1024); //<<<<< not helping !!!
sock.setSendBufferSize(10*1024+1024);
this.sock = sock;
sockInput = sock.getInputStream();
sockOutput = sock.getOutputStream();
this.myThread = new Thread(this);
}
None is allowing me to handle large so that i can switch from UDP to TCP. Any ideas!!
TCP and UDP are different. TCP is not a packet-based protocol, it is a stream based protocol. Your data will arrive in order, and un-corrupted, but it will not necessarily all arrive at once.
What you should do is prefix each set 'packet' with a length. When you receive data, first read the length value, and then continue reading chunks from the socket until you have the amount of data required.
You shouldn't care how TCP splits your messages into packets and how many times you must read to get a full message. Just read until the end of the message has been reached, and then return all the bytes of the message.
Knowing when the message has been read completely depends on your protocol. You could choose to send only one message per connection, or you could send the length of the message before the message, or use some marker byte.
Note that you shouldn't use new String(byte[]) without specifying an encoding (and use the same when calling String.getBytes()), especially if you transfer from one machine to another, since both machines could have a different default encoding.
The trouble you're having is how InputStreams work. When you call read(buf,offset,length) it doesn't always read length bytes everytime. It simply reads what is available() on the InputStream, and returns the number of bytes it read. So you need to continue to call read until you've actually read length bytes.
int len = 0;
int expectedLength = sockInput.readInt();
byte[] buf = new byte[ expectedLength ];
while( len != buf.length ) {
len += sockInput.read( buf, len, buf.length );
}
// now you have a buffer with expectedLength data in it.
You can also look at using DataInputStream.readFully() by wrapping your
DataInputStream ds = new DataInputStream( sock.getInputStream() );
ds.readFully( buf, 0, buf.length );
http://docs.oracle.com/javase/6/docs/api/java/io/DataInputStream.html
But if you're really just after a String you should do this:
Reader reader = new InputStreamReader( sockInput.getInputStream(), "UTF-8" );
Same rules for read( char[], offset, length ) as read( byte[], offset, length )
A TCP is a stream of bytes you can get a portion of, one or more messages is a single read.
With UDP packets of more than 532 bytes, the packet can be broken up, so you have the same potential problem.
A simple way to encode message is to send the length first.
final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
final DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
public void writeMessage(String text) {
out.writeUTF(text);
}
public String readMessage() {
return in.readUTF();
}
read/write UTF sends the length first and followed by the content