I've written a basic udp client server where one android sends message to the server and server relays it to the rest of the clients.
The issue is, the incoming udp messages get to the server and server relays them back but they never reach the rest of the devices.
Whats really amazing is that if I use the server as echo server (i.e relaying only to sender) then everything works. All client and server sockets use same port 2706
Server code
while (true) {
DatagramPacket packetToReceive = new DatagramPacket(new byte[2048], 2048);
try {
listenerSocket.receive(packetToReceive);
InetAddress senderAddress = packetToReceive.getAddress();
relayedBytes += packetToReceive.getLength();
if (!connectedClients.contains(senderAddress)) {
connectedClients.add(senderAddress);
}
for (InetAddress addr : connectedClients) {
// commenting this line will make it an echo server
if (!addr.equals(senderAddress))
{
//The following actually prints the ips of the android
//devices so it knows where to send
System.out.println(senderAddress.getHostAddress().toString() +
" to " + addr.getHostAddress().toString());
byte[] data = packetToReceive.getData();
packetToReceive.setData(data, 0, packetToReceive.getLength());
packetToReceive.setAddress(addr);
listenerSocket.send(packetToReceive);
}
}
} catch (IOException) {
e.printStackTrace();
}
}
android sender logic:
mainSocket=new DatagramSocket(homePort);
//targetAddressString is the public IP of server
target = InetAddress.getByName(targetAddressString);
while (true) {
byte[] data = getdata();
if (data == null)
continue;
DatagramPacket packet = new DatagramPacket(data, data.length, target, targetPort);
mainSocket.send(packet);
}
meanwhile on other thread the reciever just waits with the same udp socket:
while (true) {
Log.d("Player", "Waiting for data");
DatagramPacket packet = new DatagramPacket(new byte[2048], 2048);
try {
mainSocket.receive(packet);
Log.d("Player", "packet received");
//do something with the packet
} catch (IOException e) {
e.printStackTrace();
}
}
It never moves further than waiting for data since it'll block until it receives a packet
Moreover I can also see it in the wifi and Mobile data icons that no data is ever received but data sending is always on and is seen received on the server
**EDIT:- Echo server **
while (true) {
DatagramPacket receivedPacket = new DatagramPacket(new byte[2048], 2048);
try {
listenerSocket.receive(receivedPacket);
InetAddress senderAddress = receivedPacket.getAddress();
if (!connectedClients.contains(senderAddress)) {
connectedClients.add(senderAddress);
}
for (InetAddress addr : connectedClients) {
byte[] data = receivedPacket.getData();
DatagramPacket sendPacket= new DatagramPacket(data, 0, receivedPacket.getLength(), addr, receivedPacket.getPort());
listenerSocket.send(sendPacket);
}
} catch (IOException e) {
// ...
}
}
Basically it should relay the message to every single client who've ever sent any data but somehow it only sends its to its original sender the rest of clients miss it. the code is trolling with me
This is the NAT traversal problem as you have figured out.
Here's a few hints:
The server can be hardcoded to listen on port 2706. But it shouldn't make any assumptions about what source port of received packets are. From your code, it doesn't look like you ever attempt to call setPort. So even if the NAT wasn't mapping your port number differently, I'm not sure how your original code was even getting the any destination port set. But I think you figured this out based on your own answer and your updated code.
Don't hardcode a client port on the client's socket. Choose port "0" (or don't set one) on your socket to let the OS choose the first available port for you. If you have multiple clients behind the same NAT, this behavior will allow for quicker client restarts, multiple devices behind the same NAT, and other nice things.
Holly Molly! I finally figured it out , it was my own fault I wasn't considering my cellphone provider's Nat and was assuming 2706 port in public IP as well.
Turns out actually I was under my cellphone network's NAT and my port 2706 was converted to some hideous port number by the time it reached the server. So I had to consider the port no of the actual packet received rather than the one set on the phone.
In a nutshell it was like this
cellphone (port 2706)-> NAT (port 40234) -> Server (port 40234)
and so I was actually trying to send back data to 2706 instead of 40234 -_-'
Once I start to send back the packets at 40234 (or whatever came with the packet) instead of 2706 , it gracefully followed the path back to my android cellphone and everything was fine.
Related
There are two Groovy sub programs below sending messages to each other via plain UDP sockets. They does receive the messages successfully when they are sent to 127.0.0.1. But the messages aren't received when sending them to the public IP address (the machine is behind NAT).
Why the hole is not punched? And how to fix that?
I tried querying a public STUN server via a Java library earlier but it responded with the same public IP address to me, so I use wtfismyip.com here.
class GroovyTest {
static String PUBLIC_IP = new URL('https://wtfismyip.com/text').text.trim()
//static String PUBLIC_IP = '127.0.0.1' // works fine
static void main(String[] args) {
runInstance(11111, 22222)
runInstance(22222, 11111)
}
static void runInstance(int thisPort, int anotherPort) {
def socket = new DatagramSocket(thisPort)
Thread.start {
// message listener
byte[] buf = new byte[1024]
while (true) {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
InetAddress remoteAddr = packet.getAddress();
int remotePort = packet.getPort();
String sentence = new String(packet.getData(), 0, packet.length);
println("server-$thisPort: received [$sentence] from ${remoteAddr.hostAddress}:${remotePort}")
}
}
Thread.start {
// message sender
while (true) {
println("client-$thisPort: sending to ${PUBLIC_IP}:${anotherPort}...")
byte[] buf = ("Hello " + System.currentTimeMillis()).bytes
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(PUBLIC_IP), anotherPort)
socket.send(packet)
Thread.sleep(2000)
}
}
}
}
Your problem stems from the fact that the IP address returned by wtfismyip is the IP address of the router on your network, which is not assigned to your computer. When you try and send a datagram to your router's public IP you'll probably get and ICMP Destination Unreachable error message from your router. If you need this behavior, your router may have some port forwarding capabilities that can forward inbound UDP traffic to your local IP address.
I have been successfully replying to UDP packets behind a NAT router by simply taking the address and port details from the UDP packet I'm responding to...
DatagramSocket socket = new DatagramSocket(port);
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(receivePacket);
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length,
receivePacket.getAddress(), receivePacket.getPort());
socket.send(sendPacket);
The code is more robust in that it doesn't matter where the packet came from, or any address translation which occurred along the way. It will always reply to the right place.
I'v also noticed you are using two different port numbers. "thisPort" and "anotherPort". As far as I know, the hole punching will only work if you reply on the same port number. This makes sense for security reasons.
The marine robot on top of my head, pictured in my avatar, uses this UDP hole punching technique.
It's my first post so it may be not well stylished but I tried...
So... I have two machines, both running Java. I want them to run something like this.
Client: sends multicast to listening servers.
Server(s): the server captures the multicast and sends a unicast back with name of local machine that server runs on.
Client: receives the unicast with the server adress(es) and makes a list with their hostnames.
But the client doesn't even send the multicast (I was watching wireshark capturing packets)
It only sends something when I put 230.0.0.1 as multicast address, but then, the server doesn't receive the packet.
EDIT: When I send a unicast packet to the server it responds fine.
Here is my code:
try
{
//The client runs on LeJOS EV3 so I used their classes a bit
LCDOutputStream lcd = new LCDOutputStream();
PrintStream p = new PrintStream(lcd);
while(true)
{
if(Button.waitForAnyPress() == Button.ID_ESCAPE)
{
break;
}
byte[] buf = this.writeString("get_ip");
DatagramSocket sender = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("230.0.0.1"), 5555);
sender.send(packet);
sender.close();
p.println("Sent Multicast");
}
p.close();
lcd.close();
}
catch(Exception e)
{
console.printException(e);
}
Here is the server code:
MulticastSocket s = new MulticastSocket(5555);
s.joinGroup(InetAddress.getByName("225.1.1.1"));
while(true)
{
try
{
/*
* 225.1.100.1
*
DataSender.Impl.reply("225.1.100.1", 5555, InetAddress.getLocalHost().getHostName(), "get_ip");*/
byte[] buf = new byte[256];
DatagramPacket p = new DatagramPacket(buf, buf.length);
s.receive(p);
System.out.println("DEBUG: received request");
}
catch(IOException e)
{
e.printStackTrace();
break;
}
}
s.close();
The comments made by ecle in response to the following post helped me resolve a similar issue: Java Multicast sample program is unable to deliver packets within LAN (across hosts). In my case, adding setInterface(<server address>); worked. For example:
MulticastSocket s = new MulticastSocket(5555);
s.setInterface(<server address>);
s.joinGroup(InetAddress.getByName("225.1.1.1"));
I want to do UDP Hole Punching with two clients with the help of a server with a static IP. The server waits for the two clients on port 7070 and 7071. After that it sends the IP address and port to each other. This part is working fine. But I'm not able to establish a communication between the two clients. I tried the code in different Wifi networks and in 3G mobile network. The client program throws the IO-Exception "No route to host".
The client code is used for both clients. Once executed with port 7070 and once with 7071.
Do you think I've implemented the UDP hole punching concept correctly? Any ideas to make it work?
Here's the server code first, followed by the client code.
Thank you for help.
Code of server:
public class UDPHolePunchingServer {
public static void main(String args[]) throws Exception {
// Waiting for Connection of Client1 on Port 7070
// ////////////////////////////////////////////////
// open serverSocket on Port 7070
DatagramSocket serverSocket1 = new DatagramSocket(7070);
System.out.println("Waiting for Client 1 on Port "
+ serverSocket1.getLocalPort());
// receive Data
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
serverSocket1.receive(receivePacket);
// Get IP-Address and Port of Client1
InetAddress IPAddress1 = receivePacket.getAddress();
int port1 = receivePacket.getPort();
String msgInfoOfClient1 = IPAddress1 + "-" + port1 + "-";
System.out.println("Client1: " + msgInfoOfClient1);
// Waiting for Connection of Client2 on Port 7071
// ////////////////////////////////////////////////
// open serverSocket on Port 7071
DatagramSocket serverSocket2 = new DatagramSocket(7071);
System.out.println("Waiting for Client 2 on Port "
+ serverSocket2.getLocalPort());
// receive Data
receivePacket = new DatagramPacket(new byte[1024], 1024);
serverSocket2.receive(receivePacket);
// GetIP-Address and Port of Client1
InetAddress IPAddress2 = receivePacket.getAddress();
int port2 = receivePacket.getPort();
String msgInfoOfClient2 = IPAddress2 + "-" + port2 + "-";
System.out.println("Client2:" + msgInfoOfClient2);
// Send the Information to the other Client
// /////////////////////////////////////////////////
// Send Information of Client2 to Client1
serverSocket1.send(new DatagramPacket(msgInfoOfClient2.getBytes(),
msgInfoOfClient2.getBytes().length, IPAddress1, port1));
// Send Infos of Client1 to Client2
serverSocket2.send(new DatagramPacket(msgInfoOfClient1.getBytes(),
msgInfoOfClient1.getBytes().length, IPAddress2, port2));
//close Sockets
serverSocket1.close();
serverSocket2.close();
}
Code of client
public class UDPHolePunchingClient {
public static void main(String[] args) throws Exception {
// prepare Socket
DatagramSocket clientSocket = new DatagramSocket();
// prepare Data
byte[] sendData = "Hello".getBytes();
// send Data to Server with fix IP (X.X.X.X)
// Client1 uses port 7070, Client2 uses port 7071
DatagramPacket sendPacket = new DatagramPacket(sendData,
sendData.length, InetAddress.getByName("X.X.X.X"), 7070);
clientSocket.send(sendPacket);
// receive Data ==> Format:"<IP of other Client>-<Port of other Client>"
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
clientSocket.receive(receivePacket);
// Convert Response to IP and Port
String response = new String(receivePacket.getData());
String[] splitResponse = response.split("-");
InetAddress ip = InetAddress.getByName(splitResponse[0].substring(1));
int port = Integer.parseInt(splitResponse[1]);
// output converted Data for check
System.out.println("IP: " + ip + " PORT: " + port);
// close socket and open new socket with SAME localport
int localPort = clientSocket.getLocalPort();
clientSocket.close();
clientSocket = new DatagramSocket(localPort);
// set Timeout for receiving Data
clientSocket.setSoTimeout(1000);
// send 5000 Messages for testing
for (int i = 0; i < 5000; i++) {
// send Message to other client
sendData = ("Datapacket(" + i + ")").getBytes();
sendPacket = new DatagramPacket(sendData, sendData.length, ip, port);
clientSocket.send(sendPacket);
// receive Message from other client
try {
receivePacket.setData(new byte[1024]);
clientSocket.receive(receivePacket);
System.out.println("REC: "
+ new String(receivePacket.getData()));
} catch (Exception e) {
System.out.println("SERVER TIMED OUT");
}
}
// close connection
clientSocket.close();
}
UPDATE
The code is generally working. I've tried it in two different home networks now and it's working. But it isn't working in my 3G or university network. In 3G, I verified that the NAT is mapping the two ports (the client port and by the router assigned port) together again, even after closing and opening the clientSocket. Has anyone an idea why it isn't working then?
UDP hole punching can't be achieved with all types of NAT. There is no universal or reliable way defined for all types of NAT. It is even very difficult for symmetric NAT.
Depending on the NAT behaviour, the port mapping could be different for different devices sending the UDP packets.
Like, If A sends a UDP packet to B, it may get some port like 50000. But if A sends a UDP packet to C, then it may get a different mapping like 50002. So, in your case sending a packet to server may give a client some port but sending a packet to other client may give some other port.
You shall read more about NAT behaviour here:
https://www.rfc-editor.org/rfc/rfc4787
https://www.rfc-editor.org/rfc/rfc5128
UDP hole punching not going through on 3G
For symmetric NAT (3G network connecting to a different mobile network), you need to do Multi-UDP hole punching.
See:
https://drive.google.com/file/d/0B1IimJ20gG0SY2NvaE4wRVVMbG8/view?usp=sharing
http://tools.ietf.org/id/draft-takeda-symmetric-nat-traversal-00.txt
https://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf
http://journals.sfu.ca/apan/index.php/apan/article/view/75/pdf_31
Either that or relay all the data through a TURN server.
You rightly use a rendezvous server to inform each node of the others IP / port based on the UDP connection. However using the public IP and port which is the combination which will is obtained by the connection as you have, means that in scenarios where both hosts exist on the same private network hairpin translation is required by the NAT which is sometimes not supported.
To remedy this you can send the IP and port your node believes itself to have in the message to the server (private ip / port) and include this in the information each node receives on the other. Then attempt a connection on both the public combination (the one you are using) and the one I just mentioned and just use the first one which is successfully established.
I am working on a application on MEGACO protocol on Controller side. I am sending MEGACO messages to the Media Gateway via UDP prptocol. And the Media gateway is answering the requests. When I run wireshark with specified port and IP filter wireshark shows all the captured MEGACO packets. But in my application (written in JAVA) some of the packets are not reaching. More specifically saying to my application only Transaction Reply and Transaction Reply Acknowledgement (Reference: RFC 3015) messages are not reaching.
I have tried a lot of permutations and combinations. Even I have allocated new Datagram Packet and buffer space for each receiving messages as test. But no result. My code for the udp receiver is following.
while (running) {
//do work here
try {
byte[] dpBuffer = new byte[MAX_BUFFER_SIZE];
DatagramPacket dp = new DatagramPacket(dpBuffer, MAX_BUFFER_SIZE);
this.socket.receive(dp);
byte[] temp = new byte[dp.getLength()];
System.arraycopy(dp.getData(), 0, temp, 0, dp.getLength());
System.out.println("Read data");
for(int i=0;i<temp.length;i++)
{
System.out.print((char)(temp[i]));
}
ByteArrayUtil msg = new ByteArrayUtil(temp, dp.getLength());
msgParser.parseMsg(msg);
} catch (Exception e) {
logger.error("Megaco Reader Failed to read Packet due to :" ,e);
}
}
Any help??
THanks E_net4.
As I have mentioned in my comment, I was using wrong filter in wireshark. in wireshark if you use only
"udp.port == x"
as the filter it will filter those packets having either source or destination port x. To filter those packets that have either source port == x or destination port ==x, you should use udp.srcport == x and udp.destport==xrespectively.
Thanks everyone.
I'm attempting to use Java Datagrams to create a packet stream between server and client. The problem is that, although I receive confirmation that packets are being sent, they are all lost before they reach the client listener I set up. I have it right now so that there's a timeout after 5 seconds, which happens every time I run it.
class DGServer extends Thread
{
private DatagramSocket server;
public DGServer() throws IOException
{
server = new DatagramSocket();
}
public void run()
{
try
{
server.connect(App.Local, 4200);
System.out.println("Server starting...");
int i = 0;
while (server.isConnected() && (i < 256))
{
byte[] buffer = new byte[1];
buffer[0] = (byte) ++i;
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, App.Local, 4200);
System.out.println("Sedning " + i + " to client...");
server.send(packet);
Thread.sleep(500);
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("Server Finished!");
if (! server.isClosed())
server.close();
}
}
class DGClient extends Thread
{
private DatagramSocket client;
public DGClient() throws SocketException
{
client = new DatagramSocket();
}
public void run()
{
try
{
client.connect(App.Local, 4200);
client.setSoTimeout(5000);
System.out.println("Client starting...");
int i = 0;
while (client.isConnected() && (i < 256))
{
byte[] buffer = new byte[1];
DatagramPacket packet;
packet = new DatagramPacket(buffer, 1, App.Local, 4200);
//System.out.println("Sedning " + i + " to server...");
client.receive(packet);
buffer = packet.getData();
System.out.println("Client Received:\t" + packet.getData()[0]);
Thread.sleep(500);
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("Client Finished!");
if (! client.isClosed())
client.close();
}
}
You may choose to skim over the second class. They're widely the same, it just replaces server.send, with client.receive. Also, this class was not designed to really do anything important. So, a lot of the code(like, Exception handling), is written very simplistically.
Is there anything I can do to prevent the loss of packets? I have the port forwarded on my computer(not that it should matter, I'm using my localhost, which is App.Local in case you wondered).
Also, side question. I originally had it set up as a single class, coded to send a packet, then turn around and receive one. But it threw an exception because the 'ICMP Port is unreachable'. Does anyone know why this happens?
Ok first off, I think you are testing both the server and the client at the same time, so you don't have any idea whether which one fails.
You should use either netcat (nc) or wireshark to test the client
with netcat, you can run the following command
nc -l -u -p 4200 -vv
This will tell netcat to listen (-l) on udp (-u) on port (-p 4200) and be very verbose (-vv)
This way you'll be able to check if your client can connect to anything.
You can use the same program to check if your server can receive connections from a known working program with
nc -u [target ip] 4200
There is a netcat cheatsheet here
You can also check netcat to netcat to diagnose if it is purely a network issue. Maybe the firewalls/NAT aren't configured correctly
Why both server and client doing a connect ?
Shouldn't one side be sending the data ?
Something like :
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(buf, buf.length,
address, 4200);
socket.send(packet);
It sounds to me like there is some packet filter / firewall interfering with UDP traffic between the client and server on the port that you are using. It could be simple packet filtering, it could be NAT (which interferes with UDP traffic unless you take special steps), it could be some accidental network misconfiguration.
But it threw an exception because the 'ICMP Port is unreachable'. Does anyone know why this happens?
IMO, this is more evidence of packet filtering.
(However, its also a bit unexpected that you should receive this in response to trying to sent a datagram. I'd simply expect there to be no response at all, and any ICMP responses to a UDP request to have been dropped on the floor by the OS. But, I may be wrong about this ...
Now if you were using a regular stream socket; e.g. TCP/IP, this behaviour would be understandable.)
You're not binding the sending socket to a specific port number, so the client won't be able to send to it if connected. I suspect you have the same problem in reverse as well, i.e. the client socket isn't bound to port 4200. That would explain everything.
I would get rid of the connects and use explicit port numbers when sending, and the incoming port number when replying.
I am sure you are aware that UDP is a lossy proocol and you have allowed for this. Still you should expect to get some packets.
I suggest you test whether your program works by using the client and server on the same host and on different hosts, avoiding any firewalls. If this works then you have a network configuration issue.
If you are running both of them in the same machine this is never going to work because you are connecting both (server and client) to the same port.