I'm facing UDP packet loss problem based on Android devices. I have two devices. Following code works correctly on one device. The other device lost many packages. I have already read solution of similar problem. In this solution, setting the datagram socket size to 64k is suggested. But I couldn't set it.
How can I change datagram buffer size?
My code:
DatagramSocket udpSocket = null;
try {
udpSocket = new DatagramSocket(5004);
udpSocket.setReceiveBufferSize(64*1024);
Log.d("UDPSocket", "Buffer Size : " + udpSocket.getReceiveBufferSize());
} catch (SocketException e1) {
e1.printStackTrace();
}
Log:
05-14 10:34:05.960: D/UDPSocket(28021): Buffer Size : 112640
Author of the chosen anwser seems to have problems using past tense and speak to present nearly all time, but at one point he exactly say
I removed this code setting buffer size and then it strated receving all the packets
So in fact it was changing datagram buffer size wich seems to have caused it's problem.
By the way, your method to set buffersize probably work, in fact log message respond to you with your platform buffer size, wich you can't change, see Android DatagramSocket receive buffer size.
I resolved my problem.
I changed my receive data code.
Past code:
byte[] receiveData = new byte[1328];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
udpSocket.receive(receivePacket);
} catch (IOException e) {
e.printStackTrace();
}
New code:
ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.fromDatagramSocket(udpSocket);
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
FileInputStream fin = new FileInputStream(fileDescriptor);
byte[] receiveData = new byte[1328];
int readByte = fin.read(receiveData);
Related
This question has been asked a lot, but so far, none of the solutions that I applied from previous answers have helped me.
Main goal
I am trying to learn UDP conexions and this is my attempt. I want to have a client ask for a picture at a server via UDP and the server will send it. Then the client will create a file with that information given.
Explanation
My main idea is to ask the server for an image using a "GET" command (not the HTTP, just GET) followed by the name of the image(extension included). Then the client awaits an answer which is the image requested.
Problems
The client waits and answer which does no come
Research
From another similar question it was a problem that I was using the same PORT for both receive and connect, so I added two ports, receivingPORT and sendingPORT, no results from the Client.
From other similar questions, It was a Firewall problem. So, on a Win10 machine, I created a new rule for UDP in the Firewall for the ports that I am using for this application, and nothing was received by the Client...
I have checked that the image is loaded into byte[] and the image is sent. But on the Client, nothing is received and stays there waiting for a connection to come through
CODE from Server
public class UDPserver {
static DatagramSocket serverUDP;
static DatagramPacket packet;
static InetAddress address;
static byte[] buffer = new byte[65507];//65507
final static int receivingPORT = 6668;
final static int sendingPORT = 6669;
public static void main(String[] args) throws SocketException, IOException, InterruptedException{
boolean serverActive = true;
String order = "";
String file = "";
//Instantiate server
serverUDP = new DatagramSocket(receivingPORT);
while(serverActive){
//Kind of packet we want to receive
packet = new DatagramPacket(buffer, buffer.length);
System.out.println("Server awaiting connection...");
//Receive it
serverUDP.receive(packet);
System.out.println("Received packet from: " + packet.getAddress() + "/" + packet.getPort());
//What does the packet contain?
String msg = new String(packet.getData());
address = packet.getAddress();
System.out.println("Order from: " + address + "/" + receivingPORT + " says: " + msg);
try{
order = msg.split(" ")[0].trim();
file = msg.split(" ")[1].trim();
} catch (Exception e){
}
switch(order){
case("GET"):{
System.out.println("Sending back an image...");
buffer = loadImageFromServer(file);
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
Thread.sleep(5000);
serverUDP.send(packet);
System.out.println("Client served");
break;
}
case("DISCONNECT"):{
buffer = "Server is disconnecting...".getBytes();
packet = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
serverUDP.send(packet);
serverActive = false;
serverUDP.close();
break;
}
}
}
}
static byte[] loadImageFromServer(String path) {
try {
System.out.println("Loading path: " + path);
//Instantiate a buffer from the image for it
BufferedImage img = ImageIO.read(UDPserver.class.getResource(path));
//Create a byte[] stream object to handle the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//Write the image data into those above with jpg format
ImageIO.write(img, "png", baos);
//Flush the information
baos.flush();
byte[] buffer = baos.toByteArray(); //Write it out on a byte string and return it
return buffer;
} catch (IOException ex) {
Logger.getLogger(UDPserver.class.getName()).log(Level.SEVERE, null, ex.fillInStackTrace());
System.exit(-1);
}
return null;
}
}
CODE client
public class Client {
static DatagramSocket clientUDP;
static InetAddress address;
static DatagramPacket packetSend;
static DatagramPacket packetReceive;
static int SIZE = 65507;
final static int receivingPORT = 6669;
final static int sendingPORT = 6668;
static byte[] buffer = new byte[SIZE];
static Scanner scan = new Scanner(System.in);
public static void main(String[] args) throws SocketException, UnknownHostException, IOException{
boolean clientLoop = true;
//Get address
address = InetAddress.getByName("localhost");
//Instantiate Client -> UDP
clientUDP = new DatagramSocket();
while(clientLoop){
System.out.print("Enter any key and press enter");
scan.next(); //Just to stop the loop
//Load the buffer
buffer = "GET imagenServidor.png".getBytes();
//buffer = "DISCONNECT".getBytes();
System.out.println("Buffer is ready");
//Arm the packet
packetSend = new DatagramPacket(buffer, buffer.length, address, sendingPORT);
System.out.println("Packet is armed!");
//Send the packet to the server
clientUDP.send(packetSend);
System.out.println("Order sent to server");
System.out.println("Waiting an answer");
packetReceive = new DatagramPacket(buffer, buffer.length, address, receivingPORT);
clientUDP.receive(packetReceive);
System.out.println("Server answered!");
ByteArrayInputStream bais = new ByteArrayInputStream(packetReceive.getData());
BufferedImage image = ImageIO.read(bais);
System.out.println(image);
}
clientUDP.close();
}
}
NOTES
This is a UDP exercise
The Reason
MTU!
You are sending packets with long buffe through UDP directly, which may not work in most network circumstances.
A packet sent through UDP should not be longer than the network MTU, otherwise it would be dropped. The network MTU may not be more than 1500 on most net nods(routers/switchs/hosts...), and even smaller sometimes. Though some nods may do sigmentation for ip packets, but you should not count on it when you are using UDP.
Suggestions
Use TCP instead in this application, as for:
You are sending data which expected to be complete (otherwise it would be useless).
You do not care about congestion control algorithms.
So just go with TCP.
Edit Based on The Update of The Question
So, as this is an excercise, in which you have to use UDP only.
As a file might be useless unless it is complete, you have to make sure:
All packets are possible to pass the path. Which means network should be connected both physically and virtually, and packet size should always be smaller than the MTU.
If any packets are lost, both the receiver and the sender should be able to know.
If any apckets come out of order, the receiver should be able to know.
Sender should be able to cache and resend the packets which are not confirmed by the receiver yet.
Make sure your have a good network connection. Split the image buffer into buffer array with each buffer item length less than 1000bytes(should be safe).
Then let's design an amature but simple protocol for this:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | sequence number |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
For types, we may need:
hello: 0x01
bye: 0x02
ack: 0x03
nack: 0x04
data: 0x05
feedback: 0x06
...
Sequence should be mono-increasing. e.g. 1, 2, 3, 4.... (Not necessory to start from 1 but OK)
It works like following:
Sender->Receiver: hello(seq=i)
Receiver->Sender: ack(seq=i)
# Sender->Receiver: hello(seq=i)
# if timeout and got no ack for seq=i
Sender->Receiver: data(seq=i+1)
Receiver->Sender: ack(seq=i+1)
# Sender->Receiver: hello(seq=i+1)
# if timeout and got no ack for seq=i+1
Sender->Receiver: data(seq=i+2)
Sender->Receiver: data(seq=i+3)
Receiver->Sender: ack(seq=i+2)
Receiver->Sender: ack(seq=i+3)
# Sender->Receiver: hello(seq=i+2)
# if timeout and got no ack for seq=i+2 or got nack for seq=i+2
Sender->Receiver: bye(seq=n)
Receiver->Sender: ack(seq=n)
# bye is not necessory
Firstly, I think you need to learn how to use wirshark or tcmpdump to analysis network streams when debugging, that will help you find out the problem and solve it.
As for your program, there are several problems the user207421 has mensioned. I think it's better to use TCP, but if you want to learn UDP by this way, the thing you need is to do a slim reliable UDP by yourself.
For example, you may need the following models
Build a send buffer and recive buffer, check every time if the buffer is empty, if not, send/receive and process it.(Cause UDP has MTU)
Add some extra format of information in the head of each datagram, which includes the size of the whole message, the sequence of the datagram, the left size, etc.(Cause you need to cut your message into many parts)
Build a controller, which need to have some function like retransmission, rebuild the message, etc.(Cause UDP is unreliable, you need to check the completeness of all parts)
Hope that can help you.
I just want to send data to another device (PC) using UDP by clicking on a button. So I made a method for it in which I put the UDP code and made a relation when button is clicked. But according to the IDE, the byte which contains the String data I want to send is not well created.
Here's the code:
String argv = "WhatISend";
int port = 9268;
byte[] buffer= new byte[argv.lenght()];
try {
InetAddress server = InetAddress.getByName("172.16.19.14");
buffer = argv.getBytes();
DatagramSocket socket = new DatagramSocket();
DatagramPacket udp_emission = new DatagramPacket(buffer, buffer.length, server, port);
socket.send(udp_emission);
socket.close();
} catch (Exception e) {
System.out.println("Fail socket");
}
the problem is on "byte[] buffer= new byte[size];"
Based on the wording of your question, it sounds like you want buffer to contain the characters of the original string. However, the byte array actually does not contain the string data. The code you have shown us just creates a byte array that has the same length as the input string. You have not assigned the individual characters over to the buffer.
What you probably want is this:
buffer = argv.getBytes();
For more information, see String.getBytes().
So after looking around for a suitable library, I've decided to make my own and here is part of it.
try {
DatagramSocket serverSocket = new DatagramSocket(client_port);
byte[] receiveData = new byte[1024];
byte[] finalData;
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
erverSocket.receive(receivePacket);
if (this.address == receivePacket.getAddress() && this.server_port == receivePacket.getPort()) {
** handler.onMessage(receivePacket.getData()); **
}
}
} catch (IOException ex) {
Logger.getLogger(PacketHandler.class.getName()).log(Level.SEVERE, null, ex);
This code is obviously ran asynchronously.
The receiveData is set to 1024, which is terribly wasteful. It also means that anything bigger than 1024 gets split into two or more "Events" fired by the library.
I was wondering how it is possible to make a buffer, as I've gone completely blank. Essentially, you'd have to count the number of bytes somehow and push it into a new array.
So I'm basically asking for an efficient method, which doesn't have to include code.
Are you sure that you need to use UDP for it?
Seems like you should change it to TCP impelementation. Do you want to use netty for it ? See here.
1024 bytes it's normal safe value for UDP datagaram, making it bigger can bring issues with router. (See https://stackoverflow.com/a/13089776/3502543).
If you don't want to use netty, you should provide something similar to DelimiterBasedFrameDecoder.
edited.
I want to send an object as a UDP packet and then receiving the object on the server. I have the client side figure out, but I can't get the server to read in the datagram correctly.
Client Code:
public void sendMessage() {
ByteArrayOutputStream bStream = new ByteArrayOutputStream();
try {
ObjectOutput oo = new ObjectOutputStream(bStream);
oo.writeObject(asset);
// Send it
byte[] serializedMessage = bStream.toByteArray();
DatagramPacket sendPacket = new DatagramPacket(serializedMessage,
serializedMessage.length, ipAddress, sPort);
clientSocket.send(sendPacket);
oo.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Server Failed Attempt.
public void startServer() {
try {
serverSocket = new DatagramSocket(this.serverPort);
serverSocket.receive(new DatagramPacket()); /*Code fails here, I realise
* the constructor does not have input, but I can not figure out how to init
*a buffer whose size I do not know beforehand.
*/
this.threadPool.execute(new QueryTask(packet));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I used the following question Sending Objects Across Network using UDP in Java to send the object in, but it did not show how he received said object.
Question 2:
Is it better to create a new thread once i receive and parsed out the packet or should I create a new thread with the DatagramSocket over the Datagrampacket?
Thanks In Advance.
Create a buffer that is one bigger than the largest packet you expect to receive. Then if you ever get a packet that size, it is an overflow. Note that you should re-initialize the DatagramPacket's length before every receive, otherwise it keeps shrinking to the smallest datagram received so far.
If you can process packets quickly enough you don't really need threads at all with UDP, there being no connections to handle.
As you already seen, datagramm packet needs buffer to write stuff into. I would create buffer which is just big enough for intendet data (UDP specifies 65K for IPV4 and 4G for IPV6). And if I were you , I would not use java.io serialisation as it is going to break on every class change ( or even sometimes compiler change ). Use more robust serialisation means like JSON ( with jackson / gson ) or XML or protobuf
I want to send an image through UDP on localhost Android but I don't know how to combine the image's packets. I read a jpeg file and send it to client side. I want to create this jpeg file on server side. What should I do? This my code but I have a permission denied error:
Client Side:
InetAddress serverAddr = InetAddress.getByName(Server.SERVERIP);
FileInputStream fin = new FileInputStream("/sdcard/send.jpg");
BufferedInputStream input = new BufferedInputStream(fin);
Log.d("UDP", "C: Connecting...");
et.append("C: Connecting...\n");
/* Create new UDP-Socket */
DatagramSocket socket = new DatagramSocket();
byte[] sendData = new byte[Server.BUF_SIZE];
byte[] buf = new byte[Server.BUF_SIZE];
String sentence;
int bytesRead = 0;
while((bytesRead = input.read(buf, 0, Server.BUF_SIZE)) != -1) {
sentence = new String(buf, 0, bytesRead);
sendData = sentence.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length,
serverAddr, Server.SERVERPORT);
socket.send(sendPacket);
Server Side:
File f = new File("/sdcard/out.jpg");
f.createNewFile();
FileOutputStream fos = new FileOutputStream(f);
BufferedOutputStream out = new BufferedOutputStream(fos);
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
byte[] receiveData = new byte[BUF_SIZE];
String sentence;
Log.d("UDP", "S: Connecting...");
et.append("S: Connecting...\n");
DatagramSocket socket = new DatagramSocket(SERVERPORT, serverAddr);
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
byte[] buf = new byte[Server.BUF_SIZE];
sentence = new String(receivePacket.getData());
et.append(sentence);
buf= sentence.getBytes();
out.write( buf,0, Server.BUF_SIZE );
If you're going to use UDP, you have to implement any features UDP doesn't provide that you need. Unfortunately, in this case UDP doesn't provide a long list of features that you need. There's simply no possible way this code can work.
You need, but don't provide:
1) Transmit pacing: Your code just sends data as fast as it can with no consideration for the capabilities of the network between the devices.
2) Packet ordering: Your code does not identify the packets with any kind of sequence number. If they are received out of order, the data cannot be recovered.
3) Retransmissions: If a packet is lost in transit, the data is just lost. There is no way the receiver can detect this and request a retransmission.
4) Connections: Your receiver has no way to know when the transmitter is going to start sending, when it should start receiving, when it is done receiving, and so on.
5) MTU detection: Unless you really don't care at all about performance, you want to detect the path MTU. Send datagrams that are larger than the path MTU and packet loss goes up exponentially. Send datagrams that are smaller than the path MTU and efficiency goes down.
6) Link sharing: Unless you have a link all to yourself, you need to work out a backoff and run up algorithm. Back off too much and your throughput will drop drastically when other connections use the same link. Ramp up too aggressively and you'll cause packet loss and traffic jams when you have to share a link with other traffic.
If you want to, you can implement all of these things in your own code. But TCP already has them all, and TCP has been designed, implemented, and refined by the finest minds on the planet. So it's very unlikely you would do a better job.