It appears to me that newer Android devices run behind a NAT, where the local address is an internal carrier or LAN address and the public address is the router or carrier assigned external address.
Nevertheless, newer phones don't return the same address using the NetworkInterface as when accessing an IP detection service.
Therefore, connecting via direct P2P SocketChannels inherently fails.
Are there any common workarounds to this problem designed for the Android platform? Can anyone clarify what is causing this NAT-like security issue?
Any links to Java NAT traversal tutorials or examples (NOT essays or theses) would also be appreciated as being helpful (as I'm not quite sure how to implement it in Java).
I will of course also accept any other solutions anyone has to offer!
Almost every phone or PC you will ever touch won't have a static public IP address, and therefore will require NAT traversal. It's not because of the device; the carrier or ISP put routers between your device and the public internet. Depending on your application, usually there are NAT-traversal libraries you can use, such as ice4j or STUNT.
I do that in my own project and have found this issue is not that complicated.
Here's a very simple UDP echo server in node.js
var dgram = require('dgram');
var socket =
dgram.createSocket('udp4');
socket
.on('listening', function()
{
var address = socket.address();
console.log('socket listening ' +
address.address + ':' + address.port);
})
.on('error', function(err)
{
console.log('socket error:\n' + err.stack);
socket.close();
})
.on('message', function(message, rinfo)
{
console.log('message: ' + message + ' from ' +
rinfo.address + ':' + rinfo.port);
var msg = new Buffer(rinfo.address + ':' + rinfo.port);
socket
.send(msg, 0, msg.length,
rinfo.port, rinfo.address,
function(err, bytes)
{
//socket.close();
});
})
.bind(15000);
An android client simply send a msg to this node server
System.out.println("UDP hole punching=======================");
class IOth extends Thread {
#Override
public void run() {
String sendMsg = "UDP hole punching";
byte[] buf = sendMsg.getBytes();
DatagramPacket packet;
System.out.println(HPremoteHost); // node server IP
System.out.println(HPremotePort); // 15000
try {
packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(HPremoteHost), HPremotePort);
ds.send(packet);
} catch (Exception e) {
System.out.println("error================");
System.out.println(e);
}
}
}
IOth io00 = new IOth();
io00.start();
Android Client UDP listener to obtain general msg and your own Global ip&port via UDPholepunching
class IOLoop extends Thread {
#Override
public void run() {
try {
String msg = "Native.UDPserver.open";
SocketAddress sockAddress;
String address;
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (true) {
try {
ds.receive(packet);
sockAddress = packet.getSocketAddress();
address = sockAddress.toString();
msg = new String(buf, 0, packet.getLength());
System.out.println(msg + " received !!! by " + address);
// this case is UDP HolePunching reaction
if (address.equals(HPaddress1)) {
System.out.println(msg + "hole punched");
// So you can obtain own Global ip& port here.
// exchange this information
// `remoteHost` `remotePort` to another client
// with some method (signaling server)
}
} catch (IOException e) {
}
}
} catch (Exception e) {
}
}
}
IOLoop io00 = new IOLoop();
io00.start();
Android Client UDP sender using other client's IP remoteHost remotePort
class IOth extends Thread {
#Override
public void run() {
String sendMsg = "This is a test message";
byte[] buf = sendMsg.getBytes();
DatagramPacket packet;
try {
packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(remoteHost), remotePort);
ds.send(packet);
} catch (Exception e) {
}
}
}
IOth io00 = new IOth();
io00.start();
Look at http://sourceforge.net/projects/jnat-pmplib/
It is an implementation of NAT-PMP in java.
I've managed to establish sockets just by forwarding the sockets you're using during the connection in your router. It worked for me.
UPDATE
Find out your IP address through cmd.exe if your using Windows (ipconfig) or through a terminal session if your on Linux (ifconfig). Then connect to it through the browser and there should be a security section. Go to port forwarding and open up the ports your using when establishing you're ServerSocket and Socket. Use TCP as the protocol.
Please note that this only applies if you're trying to connect from outside your wlan.
Related
Android especially.
I will try to establish a connection between two devices (android - android) where one will create a server socket, connect the other device to the client, close the socket, and the connection between the two devices remains. So simple.
Server
#Override
public void onResume() {
super.onResume();
//// !!! only for test !!!
(new Thread(new Runnable() {
#Override
public void run() {
try {
int port = 33000;
SocketAddress allInterfaces = new InetSocketAddress("0.0.0.0", port);
ServerSocketChannel channel = MuxServerSocketChannelFactory
.openAndBindServerSocketChannel(null, allInterfaces, 3);
ServerSocket server = channel.socket();
Socket socket = server.accept();
Log.i("test", "host was connected!!!: " + socket.getInetAddress().getHostAddress());
callback.onConnected(socket);
server.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
})).start();
if (true) return;
//// !!! end test on real device !!!
}
Client
int privateServerPort = 33000;
int publicServerPort = 25000;
InetAddress privateServerIpAddr = InetAddress.getByName("172.23.9.120");
InetAddress publicServerIpAddr = InetAddress.getByName("xx.xx.xx.xx"); // correct IP address
Socket socket = new Socket(publicServerIpAddr, publicServerPort,
privateServerIpAddr, privateServerPort);
// never connected
The problem arises when these devices are not in one LAN or in one, but via a VPN. It is not possible to create this connection at this time.
I've been looking for a long time here (Stackoverflow), but it does not work for me. Some of the Libraries I tried:
Portmapper
// Discover port forwarding devices and take the first one found
List<PortMapper> mappers = PortMapperFactory.discover(networkBus, processBus);
PortMapper mapper = mappers.get(0);
// mappers always return null
Cling
final PortMapping desMapp = new PortMapping(
33000,
Tool.getLocalHost(false).getHostAddress(),
PortMapping.Protocol.TCP
);
UpnpService service = new UpnpServiceImpl(new AndroidUpnpServiceConfiguration());
RegistryListener registryListener = new PortMappingListener(desMapp) {
#Override
public synchronized void deviceAdded(Registry registry, Device device) {
super.deviceAdded(registry, device);
// this callback is never call
}
};
service.getRegistry().addListener(registryListener);
Collection<Device> all = service.getControlPoint().getRegistry().getDevices();
// the value all has 0 size
service.getControlPoint().search();
Thread.sleep(5000); // anything value
all = service.getControlPoint().getRegistry().getDevices();
// again the size is 0
Is there a really simple example of How the server and client should look?
All IP addresses and ports i know. I'm testing it on Huawei P9 Lite, Elephone P9000.
I do not work with UPnP, NAT and so on.
Thank you very much for your help.
I am trying to create a socket connection between a .Net server application and Java Client Application.
I am getting an error from the java client application:
Connection refused: connect
Notes:
Communicating with a .Net Client Application, works fine.
I have disables the windows firewall
Undoubtedly, I am running the server application in the background and then I am running the client application
Following are my server code (C#):
public class Server
{
public Server()
{
CreateListener();
}
public void CreateListener()
{
// Create an instance of the TcpListener class.
TcpListener tcpListener = null;
IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
string output;
try
{
// Set the listener on the local IP address
// and specify the port.
tcpListener = new TcpListener(ipAddress, 13);
tcpListener.Start();
output = "Waiting for a connection...";
}
catch (Exception e)
{
output = "Error: " + e.ToString();
MessageBox.Show(output);
}
}
}
and client application code (Java):
public class smtpClient {
public void Send() {
Socket smtpSocket = null;
DataOutputStream os = null;
DataInputStream is = null;
try {
smtpSocket = new Socket("localhost", 13); // FAILURE
os = new DataOutputStream(smtpSocket.getOutputStream());
is = new DataInputStream(smtpSocket.getInputStream());
} catch (UnknownHostException e) {
System.err.println("Don't know about host: hostname");
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
It fails at the following line in the Java Client Application:
smtpSocket = new Socket("localhost", 13);
I can't tell what is the issue you are facing, but you need to start with a solid foundation to discover these issues.
As a rule of thumb, you should always write one piece (typically the server) first and verify connectivity (say using telnet) and then write the other piece (typically client) and verify its connectivity.
I always keep a Standard Client and Server handy to test whether its my code or its the environment/configuration.
Below is a sample code that works fine to test connectivity.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
class ClientServer {
static void Main() {
new Thread(() => { StartServer("localhost", 5013); }).Start();
Thread.Sleep(100);
Console.WriteLine("\nPress enter to start the client...");
Console.ReadLine();
StartClient("localhost", 5013);
}
public static void StartServer(string serverInterface, int port) {
try {
IPHostEntry hostInfo = Dns.GetHostEntry(serverInterface);
string hostName = hostInfo.HostName;
IPAddress ipAddress = hostInfo.AddressList[0];
var server = new TcpListener(ipAddress, port);
server.Start();
Console.WriteLine($"Waiting for a connection at {server.LocalEndpoint}");
Console.WriteLine("Press ctrl+c to exit server...");
while (true) {
TcpClient client = server.AcceptTcpClient();
Console.WriteLine($"Server says - Client connected: {client.Client.RemoteEndPoint}");
ThreadPool.QueueUserWorkItem((state) => {
using (var _client = (TcpClient)state)
using (NetworkStream stream = _client.GetStream()) {
string msg = stream.ReadAsciiData();
if (msg == "Hello!") {
stream.WriteAsciiData($"Time:{DateTime.Now: yyyy/MM/dd HH:mm zzz}. Server name is {hostName}");
}
}
}, client);
}
} catch (Exception e) {
Console.WriteLine(e);
}
}
public static void StartClient(string serverInterface, int port) {
Console.WriteLine("Client started...");
try {
using (var client = new TcpClient(serverInterface, port))
using (NetworkStream stream = client.GetStream()) {
Console.WriteLine("Client says - Hello!");
stream.Write(Encoding.ASCII.GetBytes("Hello!"));
string msg = stream.ReadAsciiData();
Console.WriteLine($"Client says - Message from server: Server#{client.Client.RemoteEndPoint}: {msg}");
}
} catch (Exception e) {
Console.WriteLine(e);
}
Console.WriteLine("Client exited");
}
}
static class Utils {
public static void WriteAsciiData(this NetworkStream stream, string data) {
stream.Write(Encoding.ASCII.GetBytes(data));
}
public static string ReadAsciiData(this NetworkStream stream) {
var buffer = new byte[1024];
int read = stream.Read(buffer, 0, buffer.Length);
return Encoding.ASCII.GetString(buffer, 0, read);
}
public static void Write(this NetworkStream stream, byte[] data) {
stream.Write(data, 0, data.Length);
}
}
Now to your specific problem,
The choice of port 13, is not ideal for testing. Usually all ports below 1024 are considered privileged. i.e. a firewall or antivirus might block your attempt to listen on that port
Remember that IPV6 addresses plays a role. Your machine might have that enabled or disabled based on your configuration. You want to make sure that if your server is listening on a IPv6 interface, then your client also connects on the same
Which brings us to another related point: Irrespective of you are using IPv6 interface or not, the client needs to connect to the same interface the server is listening on. This might seem obvious, but is often missed. A typical machine
has at-least 2 interfaces: One for localhost (127...* called loopback interface) and another non local (typically 10...* or 192...*, but not restricted to it). It can so happen (especially when you pick the first available interface to bind your server without knowing which one it is) that server might be listening on non loopback interface like say 192.168.1.10 interface and the client might be connecting to 127.0.0.1, and you can see why the client will get "connection refused" errors
The sample code above works and you can test your code with it. You can us telnet for a client or just my sample code. You can play around changing the serverInterface values to some surprising discoveries which are accentuated by
ipAddress = hostInfo.AddressList[0] line
Hope this helps you with your debugging
I tried setting the Multicast host as 230.0.0.1 using the port 5500. Then, on the other side I said to join group 230.0.0.1 at port 5500. It joined and it received packets for a few seconds. Then it stops all of a sudden. If I use 255.255.255.255 it receives packets normally. Why is this happening? The code for the Multicast sender is below:
private class StatusBroadcasterThread extends Thread
{
private static final boolean DEBUG = App.DEBUG;
private static final String TAG = "StatusBroadcasterThread";
private DatagramSocket broadcastSocket;
public StatusBroadcasterThread(int port) throws SocketException {
broadcastSocket = new DatagramSocket(port);
this.start();
}
#Override
public void run() {
while (!this.isInterrupted()) {
try {
byte[] buffer = status.toString().getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(App.Config.multicastAddress),
App.Config.multicastPort);
broadcastSocket.send(packet);
if (DEBUG)
Log.d(TAG, "Sent: " + new String(packet.getData()));
} catch (IOException e) {
Log.e(TAG, "Error: " + e.getMessage());
}
try {
sleep(App.Config.broadcastInterval);
} catch (InterruptedException ex) {
}
}
}
}
Receiver thread:
private class ReceiverThread extends Thread
{
private static final String TAG = ComMessageReceiver.TAG + "Thread";
private WifiManager wifiManager;
private MulticastSocket multicastSocket;
private InetSocketAddress groupInetSocketAddress;
private boolean joinedGroup = false;
public ReceiverThread(String group, int port, int timeout) throws IOException {
super();
wifiManager = (WifiManager) App.context.getSystemService(Context.WIFI_SERVICE);
groupInetSocketAddress = new InetSocketAddress(InetAddress.getByName(group), port);
multicastSocket = new MulticastSocket(port);
multicastSocket.setSoTimeout(timeout);
}
public ReceiverThread() throws IOException {
this(Config.multicastAddress, Config.multicastPort, DEFAULT_TIMEOUT);
}
#Override
public void run() {
Log.d(TAG, "started");
while (!this.isInterrupted()) {
if (wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
if (!joinedGroup) {
try {
multicastSocket.joinGroup(groupInetSocketAddress,
NetworkInterface.getByInetAddress(getWifiInetAddress()));
wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
wifiManager.createMulticastLock(TAG).acquire();
joinedGroup = true;
} catch (IOException ex) {
Log.e(TAG, "Failed to join Multicast group: " + ex.getMessage());
}
}
try {
byte[] buffer = new byte[256];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
multicastSocket.receive(packet);
Message message = new Message(packet);
Log.d(TAG, "message from " + message.getIp() + " " + message.getMsg());
for (MessageListener listener : listenerList)
listener.onMessageReceived(message);
} catch (SocketTimeoutException ex) {
Log.e(TAG, "Timed out: " + ex.getMessage());
} catch (IOException ex) {
Log.e(TAG, ex.getMessage());
}
} else
joinedGroup = false;
}
}
InetAddress getWifiInetAddress() throws UnknownHostException {
ByteBuffer wifiRawAddress = ByteBuffer.allocate(4);
wifiRawAddress.order(ByteOrder.LITTLE_ENDIAN).putInt(wifiManager.getConnectionInfo().getIpAddress());
return InetAddress.getByAddress(wifiRawAddress.array());
}
}
1. 255.255.255.255 is NOT a multicast address but BroadCast address.
2. Please check that you are properly closing the sockets when the communication is completed.
See below the list of all the multicast address..........
224.0.0.0 Base address (reserved)
224.0.0.1 The All Hosts multicast group addresses all hosts on the same network segment.
224.0.0.2 The All Routers multicast group addresses all routers on the same network segment.
224.0.0.4 This address is used in the Distance Vector Multicast Routing Protocol (DVMRP) to address multicast routers.
224.0.0.5 The Open Shortest Path First (OSPF) All OSPF Routers address is used to send Hello packets to all OSPF routers on a network segment.
224.0.0.6 The OSPF All D Routers address is used to send OSPF routing information to designated routers on a network segment.
224.0.0.9 The Routing Information Protocol (RIP) version 2 group address is used to send routing information to all RIP2-aware routers on a network segment.
224.0.0.10 The Enhanced Interior Gateway Routing Protocol (EIGRP) group address is used to send routing information to all EIGRP routers on a network segment.
224.0.0.13 Protocol Independent Multicast (PIM) Version 2
224.0.0.18 Virtual Router Redundancy Protocol (VRRP)
224.0.0.19 - 21 IS-IS over IP
224.0.0.22 Internet Group Management Protocol (IGMP) Version 3
224.0.0.102 Hot Standby Router Protocol version 2 (HSRPv2) / Gateway Load Balancing Protocol (GLBP)
224.0.0.107 Precision Time Protocol version 2 peer delay measurement messaging
224.0.0.251 Multicast DNS (mDNS) address
224.0.0.252 Link-local Multicast Name Resolution (LLMNR) address
224.0.1.1 Network Time Protocol clients listen on this address for protocol messages when operating in multicast mode.
224.0.1.39 The Cisco multicast router AUTO-RP-ANNOUNCE address is used by RP mapping agents to listen for candidate announcements.
224.0.1.40 The Cisco multicast router AUTO-RP-DISCOVERY address is the destination address for messages from the RP mapping agent to discover candidates.
224.0.1.41 H.323 Gatekeeper discovery address
224.0.1.129 - 132 Precision Time Protocol version 1 time announcements
224.0.1.129 Precision Time Protocol version 2 time announcements
I have searched everywhere to find an answer for this question:
I have a TCP client on my android application that sends an message to the server which is written in Visual Basic .NET Framework 4.
Now i want to send an message from my server to the phone over 3g, it works on wifi and 3g..
private class startserver extends Thread
{
public void server() throws Exception
{
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(8765);
while(true)
{
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient =
new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
clientSentence = inFromClient.readLine();
System.out.println(clientSentence.substring(1));
msgshower = clientSentence.substring(1);
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "Received: " + msgshower , Toast.LENGTH_LONG).show();
}
});
capitalizedSentence = clientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence);
}
}
#Override
public void run() {
try {
server();
} catch (Exception e) {
e.printStackTrace();
}
}
I start it in the OnCreate method
Now i send a message with (VB.NET)
Private Sub sends(ByVal message As String)
Dim tcp As New TcpClient
tcp.Connect(connectedIP, 8765)
Dim bw As New IO.BinaryWriter(tcp.GetStream)
bw.Write(message)
bw.Close()
tcp.Close()
End Sub
On wifi it will arrive, on 3g it wont... any idea's how to do this?
How do other applications archive this?
I think you're having problem with the ip address asigned by your mobile phone operator. The fact that works on wifi, but not on 3G, I think that is because your mobile(when connected through 3G) doesn't have a public IP address.
When you use SocketServer in your mobile, you're opening a port a waiting for others to connect to it. If your IP address is not reachable from internet, it won't happen (it's like having a computer behind a firewall.)
Could you try to implement the server in the VB machine, assuming that it has a public reachable address? This way, the phone wouldn't act as a server, it wouldn't be necessary to have a reachable address, as long as the VB machine has one. Then, you should use Socket class to bind to the server ip and port.
Totally confused by your code list above..
If you want to host a server in VB.NET, you should not use TcpClient class but TcpListener and if you need a better performance, use Socket class directly.
At the Android client side, you should new Socket(server,servPort), when you want to send message, write the outputStream, and read the inputStream to receive message.
I'm currently using a Java implementation of the Reliable UDP protocol, found here. The project has absolutely no tutorials so I have found it really hard to identify problems.
I have set up a client and server. The server runs on localhost:1234 and the client runs on localhost:1235. The server is first established, and loops listening for connections -
try {
ReliableSocket clientSocket = server.socket.accept();
InetSocketAddress clientAddress = (InetSocketAddress) clientSocket.getRemoteSocketAddress();
Logger.getLogger("ServerConnectionListener").info("New Connection from "+
clientAddress.getHostName()+":"+clientAddress.getPort()+" Processing...");
LessurConnectedClient client = new LessurConnectedClient(clientSocket);
ClientCommunicationSocketListener listener = new ClientCommunicationSocketListener(this, client);
clientSocket.addListener(listener);
} catch (Exception e) {
e.printStackTrace();
}
When a connection is established, it creates a listener for events on that socket -
class ClientCommunicationSocketListener implements ReliableSocketListener {
ServerConnectionListener connectionListener;
LessurConnectedClient client;
public ClientCommunicationSocketListener(ServerConnectionListener connectionListener, LessurConnectedClient client){
this.connectionListener = connectionListener;
this.client = client;
}
#Override
public void packetReceivedInOrder() {
connectionListener.server.handlePacket(client);
}
#Override
public void packetReceivedOutOfOrder() {
connectionListener.server.handlePacket(client);
}
}
When a packet is received, it passes it to server.handlePacket, which performs a debug routine of printing "Packet Received!".
My client connects to the server as so -
LessurClient client = new LessurClient();
InetSocketAddress a = (InetSocketAddress) server.getSocket().getLocalSocketAddress();
Logger.getLogger("client-connector").info("Trying to connect to server "+
a.getAddress().toString()+":"+
a.getPort());
client.connect(a.getAddress(), a.getPort());
// LessurClient.connect
public void connect(InetAddress address, int port){
try {
socket = new ReliableSocket(address, port, InetAddress.getLocalHost(), 1235);
isConnected = true;
Logger.getLogger("LessurClient").info("Connected to server "+address.getHostAddress()+":"+port);
} catch (IOException e) {
e.printStackTrace();
}
}
I have linked my code so when I press the key 'Z', it will send a packet to the server as so -
public void sendPacket(GamePacket packet){
if(!isConnected){
Logger.getLogger("LessurClient").severe("Can't send packet. Client is not connected to any server.");
return;
}
try {
OutputStream o = socket.getOutputStream();
o.write(packet.getData());
o.flush();
Logger.getLogger("LessurClient").info("Sending Packet with data \""+packet.getData()+"\" to server "+socket.getInetAddress().toString()+":"+socket.getPort());
} catch (IOException e) {
e.printStackTrace();
}
}
My problem is, after sending 32 packets, the server no longer receives packets, and after sending 64 packets, it crashes. I have investigated into the code, and it appears that its something associated with packets not being removed from the receive queue, as when I changed the _recvQueueSize variable in ReliableSocket.java:1815 from 32 to 40, I could now send 40 packets without something going wrong.
Could someone help me identify this issue? I've been looking at the code all day.
I managed to fix the problem.
You see, since this is an implementation of RUDP, it extends most of the Socket classes. Specifically, ReliableSocket.getInputStream(), was custom coded to a managed input stream. My problem was, I was receiving the packets, but not reading from the buffer.
When you receive a packet you're supposed to read from the buffer, otherwise the packet will not be dropped from the queue.
So all I had to do, was everytime I received a packet, read the size of the packet, and continue.