Send multiple very small packets or fewer large packets? - java

I am currently working on a simple application that transfers screenshots across sockets. I get the screenshot by instantiating and using the Robot class as follows:
private Robot robot;
public Robot getRobot(){
if(robot == null){
try{
robot = new Robot();
}catch(Exception e){}
}
return robot;
}
public BufferedImage screenshot(){
return getRobot().createScreenCapture(getScreenRectangle());
}
public byte[] getBytes(BufferedImage image){
byte[] data = null;
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "PNG", baos);
data = baos.toByteArray();
baos.close();
}catch(IOException e){}
return data;
}
I then use the getBytes method above to convert the BufferedImage to a byte array which is then written to the socket output stream. The image is an average of 500KB. Would it be more efficient to split this 500KB into smaller segments of say 5KB or would it be better to keep it in larger chunks of say 30KB. My main aim here is speed and accuracy of delivery. I would also appreciate any reasoning of why either way would be more effective in these terms that the other.

Send multiple very small packets or fewer large packets?
This is a very common question in apps that QoS play an important role. I think that there isnt a correct answer, only an implemetation that adapts better to your requeriments.
A few aspects that you might consider:
Bigger packets reduces de % of overhead over data.
Bigger packets has a bigger impact when there is an error at recieving or sending data (corrupted data).
Smaller packets should be used for an application that provides better response to user.
In some applications where the traffic is important and necessary to process information quickly, instead of sending the entire image for each frame, portions of the image varied should be sent, using appropriate protocols.

The network packet size is limited by what is called MTU, you can't send a packet bigger than mtu. The mtu, as you can check from the link, is not that big (I believe 1500 bytes is the more common MTU for Ethernet). This is for the network side; on the java side deciding the segment size could depend on the number of images transmitted concurrently (if you send 100 image concurrently you have 100 segments allocated).
My suggestion is to try with a segment size slightly less that MTU (I guess you are using TCP/IP so you must consider TCP and IP header size) and see what happens.
Edit: some comments point out (correctly) that form the java perspective the MTU does not affect how the data should be chunked; this is true for packet bigger than MTU since the TCP/IP layer breaks larger chunks of data in smaller units; but the poster wants to know if there is a "best" buffer size; and the response is that over the MTU there is no benefit (for network transmission) in increasing the buffer size at the java side

Melli has great reasoning in their answer about why you'd choose larger or smaller packets. You're basically trading throughput for responsiveness.
I don't agree that there isn't a correct answer; at least in this case I think there's very obviously a better answer: If you want it to arrive at the other end as fast as possible, send it all at once!
Less system calls to send the data
Less overhead because you're sending less TCP packets & less redundant headers
Fragmentation is the responsibility of the lower levels of the network stack, not your application
The resulting code will be much more simple

Related

Socket communication between Java and C: Good buffer size

I have to implement a socket communicatio between a Server written in Java and a Client written in C.
The maximum amount of data that I will have to transmit is 64KB.
In the most socket communication tutorials they are working with buffer sizes of about 1024 Byte or less.
Is it a (maybe performance) problem to set the buffer to 64KB?
The two software parts will run on the same machine or at least in the same local area network.
And if it is a problem: How to handle messages that are bigger than buffer in general?
The buffer can be smaller than the messages without any problem while the receiver consumes the data as fast as the sender generates it. A bigger buffer lets your receiver to have more time to process the message, but usually you don't need a giant buffer: for example, when you download software the size of a file can be more than 1GB, but your browser/ftp client just reads the buffer and stores the data in a file in your local hard disk.
And in general, you can ignore the language used to create the client or the server, only the network protocol matters. Every language has its own libraries to handle sockets with ease.
I suggest a larger buffer but I suspect you see less than 5% difference whether you use 1 KB or 64 KB.
Note: b = bit and B = byte, k = 1000 and K = 1024 and it is best not to get the confused (not that it is likely to matter here)

Netty Adaptive UDP Multicast Support

Newbies having trouble processing a UDP video stream using Netty 3.2.4. On different machines we see dropped bytes etc using Netty. We have a little counter after Netty gets the bytes in, to see how many bytes are received. The variance is more than what just what UDP unreliability would account for. In our case, we also save the bytes to a file to playback the video. Playing the video in VLC really illustrates the dropped bytes. (Packet sizes being sent were around 1000 Bytes).
Questions
Are our assumptions of the Netty API correct in terms of not being able to use the AdaptiveReceiveBufferSizePredictor for UDP stream listener?
Is there a better explanation of the behavior we're seeing?
Is there a better solution? Is there a way to use an adaptive predictor with UDP?
What We've Tried
...
DatagramChannelFactory datagramChannelFactory = new OioDatagramChannelFactory(
Executors.newCachedThreadPool());
connectionlessBootstrap = new ConnectionlessBootstrap(datagramChannelFactory);
...
datagramChannel = (DatagramChannel) connectionlessBootstrap.bind(new
InetSocketAddress(multicastPort));
datagramChannel.getConfig().setReceiveBufferSizePredictor(new
FixedReceiveBufferSizePredictor(2*1024*1024));
...
From documentation and Google searches, I think the correct way to do this is to use a OioDatagramChannelFactory instead of a NioDatagramChannelFactory.
Additionally, while I couldn't find it explicity stated, you can only use a FixedReceiveBufferSizePredictor with the OioDatagramChannelFactory (vs AdaptiveReceiveBufferSizePredictor). We found this out by looking at the source code and realizing that the AdaptiveReceiveBufferSizePredictor's previousReceiveBufferSize() method was not being called from the OioDatagramWorker class (whereas it was called from the NioDatagramWorker)
So, we originally set the FixedReceivedBufferSizePredictor to (2*1024*1024)
Observed Behavior
Running on different machines(different processing power) we're seeing a different number of bytes being taken in by Netty. In our case, we are streaming video via UDP and we are able to use the playback of the streamed bytes to diagnose the quality of the bytes read in (Packet sizes being sent were around 1000 Bytes).
We then experimented with different buffer sizes and found that 1024*1024 seemed to make things work better...but really have no clue why.
In looking at how FixedReceivedBufferSizePredictor works, we realized that it simply creates a new buffer each time a packet comes in. In our case it would create a new buffer of 2*1024*1024 Bytes whether the packet was 1000 Bytes or 3 MB. Our packets were only 1000 Bytes, so we we didn't think that was our problem. Could any of this logic in here be causing a performance problem? For example, the creation of the buffer each time a packet comes in?
Our Workaround
We then thought about ways to make the buffer size dynamic but realized we couldn't use the AdaptiveReceiveBufferSizePredictor as noted above. We experimented and created our own MyAdaptiveReceiveBufferSizePredictor along with the accompanying MyOioDatagramChannelFactory, *Channel, *ChannelFactory, *PipelineSink, *Worker classes (that eventually call the MyAdaptiveReceiveBufferSizePredictor). The predictor simply changes the buffer size to double the buffer size based on the last packet size or reduce it. This seemed to improve things.
Not right sure what causes your performance issues but I found this thread.
It might be caused by the creation of ChannelBuffers for each incoming packet in which case you'll have to wait for Milestone 4.0.0.

Java DatagramPacket (UDP) maximum send/recv buffer size

In Java when using DatagramPacket suppose you have a byte[1024*1024] buffer. If you just pass that for the DatagramPacket when sending/receiving will a Java receive call for the DatagramPacket block until it has read the entire megabyte?
I'm asking if Java will split it up or just try to send the entire thing which gets dropped.
Normally the size limit is around 64KB for a UDP packet, but I wondered since Java's API allow for byte arrays if that is a limit and something super huge is dropped or split up and reassembled for you.
If it is dropped what API call would tell me the maximum data payload I can use in the Java call? I've heard that IPv6 also has jumbo frames, but does DatagramPacket (or DatagramSocket) support that since UDP defines the header spec?
DatagramPacket is just a wrapper on a UDP based socket, so the usual UDP rules apply.
64 kilobytes is the theoretical maximum size of a complete IP datagram, but only 576 bytes are guaranteed to be routed. On any given network path, the link with the smallest Maximum Transmit Unit will determine the actual limit. (1500 bytes, less headers is the common maximum, but it is impossible to predict how many headers there will be so its safest to limit messages to around 1400 bytes.)
If you go over the MTU limit, IPv4 will automatically break the datagram up into fragments and reassemble them at the end, but only up to 64 kilobytes and only if all fragments make it through. If any fragment is lost, or if any device decides it doesn't like fragments, then the entire packet is lost.
As noted above, it is impossible to know in advance what the MTU of path will be. There are various algorithms for experimenting to find out, but many devices do not properly implement (or deliberately ignore) the necessary standards so it all comes down to trial and error. Or you can just guess 1400 bytes per message.
As for errors, if you try to send more bytes than the OS is configured to allow, you should get an EMSGSIZE error or its equivalent. If you send less than that but more than the network allows, the packet will just disappear.
java.net.DatagramPacket buffer max size is 65507.
See
https://en.wikipedia.org/wiki/User_Datagram_Protocol#UDP_datagram_structure
Maximum Transmission Unit (MTU) size varies dependent on implementation but is arguably irrelevant to the basic question "Java DatagramPacket (UDP) maximum send/recv buffer size" as the MTU is transparent to the java.net.DatagramPacket layer.
# Mihai Danila. Because I couldn't add a comment to the above answer, that's why writing into reply section.
In continuation of your answer on MTU size, in my practice, I try to use NetworkInterface.getMTU()-40 for setting the buffer size of DatagramSocket.setSendBufferSize(). So, trying not to rely on getSendBufferSize() This is to make sure it matches different window sizes on different platforms and is universally acceptable on ethernet (ignoring dial-up for a moment). I haven't hardcoded it to 1460 bytes (1500-20-20) because on windows, the MTU size is universally 1500. However, windows platform's own window size is 8192 bytes, but I believe, by setting the SO_SNDBUF to < MTU, I am burdening the network/IP layer less, and for all the hops for routers and receivers, some overheads. Thus, reducing some latency over the network.
Similarly, for the receive buffer, I am using a max of 64K or 65535 bytes. This way my program is portable on different platforms using different window sizes.
Do you think it sounds OK? I have not implemented any tools to measure any differences but assuming that its the case based on what's out there.

How to determine ideal Datagram settings

I'm writing a Java client application that will consume high rate UDP data and I want to minimize packet loss at the host/application layer (I understand there may be unavoidable loss in the network layer).
What is a reasobaly high Buffer Size (MulticastSocket.setReceiverBufferSize())?
What is the ideal DatagramPacket buffer size? Is there a downside to using 64k?
I have very limited insight into the network topology and the sender application. This is running on Linux. TCP is not an option.
What is a reasobaly high Buffer Size (MulticastSocket.setReceiverBufferSize())?
Figure out how much your application might jitter and the rate of data you need to receive. e.g. if your application pauses to do something for 0.5 seconds (like garbage collection), and you're receiving data at 10MB/sec, you'd need a buffer of 5MB to make up for not receiving data for those 0.5 seconds.
Note that you might need to tune the net.core.rmem_max sysctl on linux to be allowed to set the buffers to the desired size(iirc you actually only get half the size of what you specify in the sysctl) , the default net.core.rmem_max might be rather low.
What is the ideal DatagramPacket buffer size? Is there a downside to using 64k?
The ideal is that of the MTU of your network, for normal ethernet, that means an UDP payload of 1472 bytes. Anything bigger is a bad idea, as it causes fragmented IP packet - IP fragmentation is generally considered a bad thing, as it causes more overhead and can cause more lost data.
Sockets end and receive buffers can be as large as you like, a megabyte or two if you want.
The maximum practical datagram size via a router is 534 bytes.

read and write method for large data in socket communication does not work reliably

I have created a socket programming for server client communication.
I am reading data using read(byte[]) of DataInputStream, also writing data using write(byte[]) of DataOutputStream.
Whenver I am sending small amount of data my program works fine.
But if I send a data of 20000 characters and send it 10 times then I am able to receive the data 8 times perfectly but not the 2 times.
So can I reliably send and receive data using read and write in socket programming?
My guess is that you're issuing a single call to read() and assuming it will return all the data you asked for. Streams don't generally work that way. It will block until some data is available, but it won't wait until it's got enough data to fill the array.
Generally this means looping round. For instance:
byte[] data = new byte[expectedSize];
int totalRead = 0;
while (totalRead < expectedSize)
{
int read = stream.read(data, totalRead, expectedSize-totalRead);
if (read == -1)
{
throw new IOException("Not enough data in stream");
}
totalRead += read;
}
If you don't know how many bytes you're expecting in the first place, you may well want to still loop round, but this time until read() returns -1. Use a buffer (e.g. 8K) to read into, and write into a ByteArrayOutputStream. When you've finished reading, you can then get the data out of the ByteArrayOutputStream as a byte array.
Absolutly -- TCP Sockets is a reliable network protocol provided the API is used properly.
You really need to check the number of bytes you receive on each read() call.
Sockets will arbiterily decide you have enough data and pass it back on hte read call -- the amount can dependon many factors (buffer size, memory availibility, network respose time etc.) most of which are unpredicatable. For smaller buffers you normally get as many bytes as you asked for, but, for larger buffer sizes read() will often return less data than you asked for -- you need to check the number of bytes read and repeat the read call for the remaining bytes.
It is also possible that something in your network infrastructure (router, firewall etc.) is misconfigred and trucating large packets.
Your problem is that in the server thread, you must call outputstream.flush(), to specify that the buffered data should be send to the other end of the communication

Categories

Resources