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.
Related
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.
I am trying to send two byte arrays over a Java socket. I have captured the data via wireshark and it shows that the first byte array sends; the second, however, doesn't send at all.
I am using Linux Mint and Oracle's JRE (not OpenJDK).
byte[] packet_one = new byte[] {(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x50};
byte[] packet_two = new byte[] {(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x78};
Socket sck;
DataInputStream dis;
DataOutputStream dos;
try {
sck = new Socket(IP_ADDRESS,PORT);
dis = new DataInputStream(sck.getInputStream());
dos = new DataOutputStream(sck.getOutputStream());
int recv_header = dis.ReadInt(); // This receives the correct response.
dos.write(packet_one); // Sends fine.
dos.write(packet_two); //Does not send.
int data_length = dis.readInt(); // No more data received.
}catch(Exception e) {
System.out.println("Error: " + e.getMessage());
}
So, dos.write(packet_one) works (confirmed by wireshark). Writing packet_two doesn't work. The data_length returns 0 since I don't receive anymore data either. No errors or exceptions get caught.
I have also tried using dos.flush(), but it doesn't make a difference.
Any ideas on why this may be the case?
Maybe you're exiting the program before there's time to send the buffered bytes?
Perhaps if you add something like:
for (;;) {
final byte x = dis.readByte();
System.out.println("got byte: " + (x & 0xFF));
}
the program won't exit, and you'll also see if there are more bytes that the server sent back. Might offer clues.
And, in case you don't know, readInt() reads for bytes and returns an int made from those bytes, which is 0 if all the for bytes read are zero. So your comment 'No more data received.' feels a bit ambiguous.
You could also try inserting a
sck.setTcpNoDelay(true);
after creating the socket. Writes should then go on the network immediately. I think.
Hope this helps.
How can i send a buffer of data whose length is 1 MB over socket in java.
What exactly i am doing is that i want to calculate upload speed of network.For that i want to send 1 MB data to server which i have written in C.
In C to receive and send data we have functions like send and recv through which we can send the desired number of bytes by passing the number of bytes to be sent.
send(connfd , client_message , `Bytes to send`, 0);
but in java i am able to send only 1 byte at a time using
int buffer[] = new int[1048576];
PrintWriter output1 = new PrintWriter(socket.getOutputStream(), true);
output1.print(buffer[1]);
so to send multiple bytes i need to call above function again and again. Is there any way in which i can pass whole buffer of 1048576 in on go.
Your PrintWriter has a print(char[] s) method (see doc). So you could instead create a new char array with the corresponding size (note: a char in Java is 2 byte long) and send that char array using that method.
But there is a better option: A look into the doc tells us we get an OutputStream from our socket. We could wrap that into a BufferedOutputStream, like this:
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte buffer[] = new byte[1024*1024];
bos.write(buffer, 0, buffer.length);
And then use bos.write(byte[], int, int) to directly send a byte array, which probably would be the most direct way to achieve what you want.
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 have to send a short string as text from client to server and then after that send a binary file.
how would I send both binary file and the string using the same socket connection?
the server is a java desktop application and the client is an Android tablet. i have already set it up to send text messages between the client and server in both directions. i have not yet done the binary file sending part.
one idea is to set up two separate servers running at the same time. I think this is possible if i use two different port numbers and set up the servers on two different threads in the application. and i would have to set up two concurrent clients running on two services in the Android app.
the other idea is to somehow use an if else statement to determine which of the two types of files is being sent, either text of binary, and use the appropriate method to receive the file for the file type being sent.
example code for sending text
PrintWriter out;
BufferedReader in;
out = new PrintWriter(new BufferedWriter
(new OutputStreamWriter(Socket.getOutputStream())) true,);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println("test out");
String message = in.readLine();
example code for sending binary file
BufferedOutputStream out;
BufferedInputStream in;
byte[] buffer = new byte[];
int length = 0;
out = new BufferedOutputStream(new FileOutputStream("test.pdf));
in = new BufferedInputStream(new FileOutputStream("replacement.pdf"));
while((length = in.read(buffer)) > 0 ){
out.write(buffer, 0, length);
}
I don't think using two threads would be necessary in your case. Simply use the socket's InputStream and OutputStream in order to send binary data after you have sent your text messages.
Server Code
OutputStream stream = socket.getOutputStream();
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(stream)
)
);
out.println("test output");
out.flush(); // ensure that the string is not buffered by the BufferedWriter
byte[] data = getBinaryDataSomehow();
stream.write(data);
Client Code
InputStream stream = socket.getInputStream();
String message = readLineFrom(stream);
int dataSize = getSizeOfBinaryDataSomehow();
int totalBytesRead = 0;
byte[] data = new byte[dataSize];
while (totalBytesRead < dataSize) {
int bytesRemaining = dataSize - totalBytesRead;
int bytesRead = stream.read(data, totalBytesRead, bytesRemaining);
if (bytesRead == -1) {
return; // socket has been closed
}
totalBytesRead += bytesRead;
}
In order to determine the correct dataSize on the client side you have to transmit the size of the binary block somehow. You could send it as a String right before out.flush() in the Server Code or make it part of your binary data. In the latter case the first four or eight bytes could hold the actual length of the binary data in bytes.
Hope this helps.
Edit
As #EJP correctly pointed out, using a BufferedReader on the client side will probably result in corrupted or missing binary data because the BufferedReader "steals" some bytes from the binary data to fill its buffer. Instead you should read the string data yourself and either look for a delimiter or have the length of the string data transmitted by some other means.
/* Reads all bytes from the specified stream until it finds a line feed character (\n).
* For simplicity's sake I'm reading one character at a time.
* It might be better to use a PushbackInputStream, read more bytes at
* once, and push the surplus bytes back into the stream...
*/
private static String readLineFrom(InputStream stream) throws IOException {
InputStreamReader reader = new InputStreamReader(stream);
StringBuffer buffer = new StringBuffer();
for (int character = reader.read(); character != -1; character = reader.read()) {
if (character == '\n')
break;
buffer.append((char)character);
}
return buffer.toString();
}
You can read about how HTTP protocol works which essentially sends 'ascii and human readable' headers (so to speak) and after that any content can be added with appropriate encoding like base64 for example. You may create sth similar yourself.
You need to first send the String, then the size of the byte array then the byte array, use String.startsWith() method to check what is being send.