Sending 1 MB Buffer of data over Socket in Android - java

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.

Related

Transferring Data Between Client and Server and Dynamically Flush

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.

Implementation Prefix Mode

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.

Java How to parse raw data from a UDP packet

I have hypothetical data coming from a UDP connection that is in binary form.
Its composed of 5 fields, with a size of 25 bits
their offsets as follows
1. 0-4 ID
2. 5-10 payload
3. 11-12 status
4. 13-23 location
5. 23-25 checksum
How do I read in this data?
DatagramSocket serverSocket = new DatagramSocket(18000);
byte[] receiveData = new byte[1024];
while (true)
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
//not sure how I should be reading the raw binary data back in
}
How would I store off this data?
DatagramPacket's getData returns the payload as a byte[]. You can use Arrays.copyOfRange to get the individual fields (I'm assuming you meant bytes in your question, not bits):
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
// get the entire content
byte telegramContent[] = receivePacket.getData();
// and slice it up:
byte ID[] = Arrays.copyOfRange(telegramContent,0,5);
byte payload[] = Arrays.copyOfRange(telegramContent,6,11);
// and so on.
}
After this step you'll have your fields as individual byte arrays, probably some postprocessing/reassembly into other types will be necessary. During this step probably the most annoying thing is that all Java's types are signed whereas in many communication protocols unsigned entities are used, so be careful during this step.
Not mentioned in your question but you might need it later on: assembly of messages for transmission. I like to use GNU Trove's TByteArrayList for that task as you don't need to worry about reserving a byte[] with the correct length before you start, just copy in bytes or message segements and once you're done call toArray and voilĂ , your byte[] is ready for transmission.

Failed to convert data from UDP packet into the initial data which is sent using Java

I'm writing a UDP Client to transfer a file to a UDP server. First I try to measure the length of the file, devided by the buffer length to be sent in UDP packet, to get the number of packets required to be sent. I send this number to the server first to acknowledge it. But on the server side, the transform from byte array of the receiving packet into the initial number just failed. Can anyone help me out of this? Here is my code on the client side:
DatagramSocket socket=new DatagramSocket();
File f = new File(filename);
long fileSize = f.length();
byte[] buffer = new byte[16384];
long packetNumber = (fileSize/(buffer.length))+1;
DatagramPacket sendPacket=new DatagramPacket(buffer,buffer.length,addr,srvPort);
String str=Long.toString(packetNumber);
buffer = str.getBytes();
socket.send(sendPacket);
And here is the code on the server side:
DatagramSocket socket=new DatagramSocket(port);
byte[] buffer=new byte[16384];
DatagramPacket receivePacket=new DatagramPacket(buffer, buffer.length);
while(true)
{
socket.receive(receivePacket);
if (receivePacket.getData().toString().trim()!=null)
{
String str=receivePacket.getData().toString();
System.out.println(str);
long pcount=Long.parseLong(str);
System.out.println(pcount+" packets to be received.");
break;
}
}
But on the server side the variable pcount can always not be resolved, and when I try to print out the str, it writes out some "[B#60991f" or sth, weird.
This code doesn't make any sense.
Most networks won't let you send a datagram over 534 bytes reliably.
At present you are sending 16384 bytes of zero value, because you aren't putting anything into the buffer: instead you are creating a new buffer after creating the DatagramPacket. So you aren't sending anything yet.
And you aren't receiving anything yet either. The result of String.trim() cannot be null. You must reset the byte array in a DatagramPacket before every receive(), because it shrinks to the size of the actual received packet, so unless you reset it it keeps getting smaller and smaller. The result of toString() on a byte array does not include its contents, so parsing it is futile.
You need to study several basic Java programming concepts: too many to answer here.
You're receiving a byte array, using toString() won't give you anything.
You should reconstruct the String from the bytes array using new String

UDP client / server ....include 16-bit message sequence number for filtering duplicates

my assignment includes sending an image file using UDP service (using java I implemented that successfully). My professor asked to include:
"The exchanged data messages must also have a header part for the sender to include 16-bit message sequence number for duplicate filtering at the receiver end"
How to do this?
I assume to create your UDP packet, you are using a ByteArrayOutputStream to generate the data. If that is the case, just Wrap a DataOutputStream on top of that ByteArrayOutputStream, and call writeInt(somesequenceNumber) before writing the image data to the stream.
on the receive side, do the opposite, wrap a DataInputStream around a ByteArrayInputStream, and call readInt() to get the sequence number. From there you can check whether you have already received this packet.
Something like
Write Side
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(sequenceNumber++);
dos.writeInt(imageDataLength);
dos.write(imageData);
dos.flush();
byte[] udpPacketBytes = baos.toByteArray();
Read Side
ByteArrayInputStream bais = new ByteArrayInputStream(udpPacketBytes);
DataInputStream dis = new DataInputStream(bais);
int sequenceNumber = dis.readInt();
if (seenSequenceNumbers.add(Integer.valueOf(sequenceNumber)))
{
int imageLength = dis.readInt();
byte[] imageData = new byte[imageLength];
dis.read(imageData);
}
where seenSequenceNumbers is some Set
For a 16-bit value I would use DataOutputStream.writeShort() and DataInputSTream readShort()/readUnsignedShort(). writeInt() and readInt() are for 32-bit values. If you want to avoid duplicates, a 32-bit value may be a better choice in any case. ;)

Categories