I'm trying to send UDP packets from an emulated device (Nexus S 4.0", 480 x 800: hdpi) to my host PC for development and testing. The sending side seems correct and doesn't encounter any errors, but Wireshark indicates they are not arriving at the host PC. I've researched this problem and all the fixes that worked for others are not working for me:
I added "uses-permission android:name="android.permission.INTERNET" to the maifest XML file. (I also have ACCESS_NETWORK_STATE but I don't think that's necessary for this.)
I am sending the packets to the host loopback address 10.0.2.2. The port is 5006, so it's not one that I should need special privileges for.
I am calling DatagramSocket.send() in a dedicated thread, not in the main thread. (I think this would throw NetworkOnMainThreadException anyway, and I'm not getting any exceptions.)
I have Telnet-ed into "localhost 5444" and issued the "redir add udp:5006:5006" command to setup UDP port forwarding on the emulator's virtual router. The command returns "OK" without error, and "redir list" returns "udp:5006 => 5006".
I've also setup UDP port forwarding (port 5006) on my host PC's router (between PC and open internet). But I don't think that should be necessary, this router is not between the emulator and the host PC.
I have disabled Windows firewall and anti-virus on the host PC.
Here is the relevant code in my MainActivity.java. The start() and stop() methods are called from button clicks (omitted because they are not part of the problem):
private static String TAG = "MainActivity";
private volatile boolean running = false;
private String ip = "10.0.2.2";
private int port = 5006;
public void start(View view) {
new Thread() {
public void run() {
byte[] bytes = "Hi from UDPSender!".getBytes();
try {
InetAddress inetAddr = InetAddress.getByName(ip);
running = true;
while (running == true) {
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, inetAddr, port);
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(false);
socket.send(packet);
socket.close();
Log.d(TAG, "Send packet to "+packet.getAddress().getHostAddress()+":"+packet.getPort());
Thread.sleep(1000);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}.start();
}
public void stop(View view) {
running = false;
}
Related
everyone!
I'm developing an Android app that allows to chat with nearby devices that have installed this app. In order to accomplish this, I'm using the Wi-Fi P2P API and Network Service Discovery to search for such nearby devices.
I've written the code for searching the nearby devices in a thread started by a Service. When a device is detected, the Service sends it (through a broadcast intent) to an Activity which displays the devices detected so far.
The detected devices are added to a recyclerView and, when the user presses one of them, a connection must be established to such device.
The Wi-Fi Direct connection gets established successfully (that is, the WifiP2pManager.connect() method succeeds) and the WIFI_P2P_CONNECTION_CHANGED_ACTION is caught.
In the broadcast receiver, when such broadcast intent is caught, the following code is executed:
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mManager.requestConnectionInfo(mChannel, connectionInfoListener); }
With the requestConnectionInfo() method I can obtain more information about the connection, such as the IP address of the device I'm trying to connect to.
To obtain such information, I provide an implementation of WifiP2pManager.ConnectionInfoListener to that method, which is denoted by the connectionInfoListener variable.
This is the code of my implementation of WifiP2pManager.ConnectionInfoListener:
private WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
InetAddress deviceIP = info.groupOwnerAddress;
int port = servicesConnectionInfo.get(device);
ConnectThread connectThread = new ConnectThread(deviceIP, port, device);
connectThread.start();
"device" is an instance variable of my implementation of BroadcastReceiver which is not important right now. What is important, instead, is the ConnectThread thread. That's the thread that handles the code necessary to connect the socket between the two devices. When I try to connect to a detected device, ConnectThread, in its run() method, creates a new instance of ChatConnection passing the IP address and the port number previously obtained to this constructor:
public ChatConnection(InetAddress srvAddress, int srvPort, String macAddress) throws IOException {
...
connSocket = new Socket(srvAddress, srvPort);
...
}
And here is where the problem occurs. When I test my app on my physical device, all I get is this exception:
W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 6770): connect failed: ECONNREFUSED (Connection refused)
Of course, I installed my app on a second physical device too, which gets successfully detected and a Wi-Fi Direct connection gets successfully established. But, when comes to this line of code:
connSocket = new Socket(srvAddress, srvPort);
that exception is thrown...
I apologize for the length of this question, but I wanted to be the clearest possible.
I really thank you in advance for any help.
EDIT: I forgot to mention the code for initializing the ServerSocket.
The ServerSocket is initialized in a thread that is started as soon as the Wi-Fi is enabled.
That is, when the WifiP2pBroadcastReceiver (an inner class of the app's Service which extends BroadcastReceiver) catches a WIFI_P2P_STATE_CHANGED_ACTION intent, it checks if the Wi-Fi is enabled and, if enabled, it starts the the thread where the ServerSocket is located:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
int statoWiFi = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (statoWiFi == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
mNsdService = new NsdProviderThread();
mNsdService.start();
}
The ServerSocket is initialized in the run() method of NsdProviderThread:
public void run() {
...
try {
server = new ServerSocket(0);
} catch (IOException ex) {
return;
}
...
while (!Thread.currentThread().isInterrupted()) {
Socket clientSocket = null;
try {
clientSocket = server.accept();
} catch (IOException ex) {
break;
}
try {
ChatConnection chatConn = new ChatConnection(clientSocket);
synchronized (connections) {
connections.add(chatConn);
}
} catch (IOException ex) {
continue;
}
}
"server" is an instance variable of NsdProviderThread declared as ServerSocket.
It looks like you just need to use the correct port number on both ends.
You're using zero, which from the documentation means:
A port number of 0 means that the port number is automatically
allocated, typically from an ephemeral port range.
So, when you create your ServerSocket, make sure it is listening on the same port that the other device uses to initiate the connection:
private static final int port = 6770;
//.....
try {
server = new ServerSocket(port);
} catch (IOException ex) {
ex.printStackTrace();
}
again! I've finally managed to get my app working. Here's what I've done:
Hard-code the port number;
When you get the group owner address in the ConnectionInfoListener implementation, make sure if it is the IP address of the device in use. If it is not, connect a client socket to the group owner address; otherwise, make your app wait for an incoming connection;
Initialize the ServerSocket as soon as possible (for example, when the app starts up).
In order to get the device actual IP address after a Wi-Fi Direct connection has been established, I've used this function which I've found in this project (which is derived by the original Android WiFiDirectdemo) in the "Utils" class:
public static String getLocalIPAddress() {
/*
* modified from:
*
* http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/
*
* */
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
String iface = intf.getName();
if(iface.matches(".*" +p2pInt+ ".*")){
if (inetAddress instanceof Inet4Address) { // fix for Galaxy Nexus. IPv4 is easy to use :-)
return getDottedDecimalIP(inetAddress.getAddress());
}
}
}
}
} catch (SocketException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
} catch (NullPointerException ex) {
Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
}
return null;
}
"p2pInt" is a private static String costant declared in the Utils class as:
private final static String p2pInt = "p2p-p2p0"
However, in my app, I've changed the "p2p-p2p0" string in "p2p-wlan0" since it looks like the network interface of my device for Wi-Fi Direct has that (different) name.
I hope this can help any developer who's trying to create an app that uses Wi-Fi Direct connectivity.
I'm currently developing an android app using SDK >= 16 which should be able to discover different android devices (later also iOS devices) in a local area network using the WiFi radio.
My first guess was to use multicast which turned out to be non functional on my Samsung Galaxy S2: packets are only received when sent from the same device.
My second guess is to actively scan the network using a limited IP address range and wait for a proper response. Unfortunately, this implies that the network uses DHCP to address the IP addresses.
None of the above solutions seem to be the perfect solution.
My current solution for my first guess:
public class MulticastReceiver extends AsyncTask<Activity, Integer, String> {
private static final String host = "224.1.1.1";
private static final int port = 5007;
private static final String TAG = "MulticastReceiver";
protected String doInBackground(Activity... activities) {
WifiManager wm = (WifiManager)activities[0].getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("mydebuginfo");
multicastLock.acquire();
String message = "Nothing";
if (multicastLock.isHeld()) {
Log.i(TAG, "held multicast lock");
}
try {
InetAddress addr = InetAddress.getByName(host);
MulticastSocket socket = new MulticastSocket(port);
socket.setTimeToLive(4);
socket.setReuseAddress(true);
socket.joinGroup(addr);
byte[] buf = new byte[5];
DatagramPacket recv = new DatagramPacket(buf, buf.length, addr, port);
socket.receive(recv);
message = new String(recv.getData());
socket.leaveGroup(addr);
socket.close();
} catch (Exception e) {
message = "ERROR " + e.toString();
}
multicastLock.release();
return message;
}
}
This code results in blocking on line socket.receive(recv); If I specify a timeout, I get a timeout exception.
Check my answer in very similar question Android Network Discovery Service (ish) before API 14
I do not belive that multicast is not working on Galaxy S2, some time ago when I was coding some network application, I made several test on many devices, some older like G1 but also on S2, S3 and Galaxy Tab 10.
But to be able to use multicast you must enable it programatically.
Have you used this piece of code?
WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
if(wifi != null){
WifiManager.MulticastLock lock = wifi.createMulticastLock("Log_Tag");
lock.acquire();
}
Check out http://developer.android.com/training/connect-devices-wirelessly/index.html It mentions two ways of finding local services- NSD and wifi direct.
I have a device which supports the OBEX Object Push Profile, this profile is based upon the Serial Port Profile. My guess is that I can use the Android Bluetooth Chat example for connecting this device to my Android Phone. But I ran into a problem, regarding the socket.accept() functionality in the android SDK. I try to accomplish to connect my phone with this device like this:
adapter = BluetoothAdapter.getDefaultAdapter();
device = adapter.getRemoteDevice("00:1B:DC:0F:EC:7E");
AcceptThread = new AcceptThread(true, adapter, device);
AcceptThread.start();
The constructor in AcceptThread is coded like this:
public AcceptThread(boolean secure, BluetoothAdapter adapter, BluetoothDevice device) {
BluetoothServerSocket tmp = null;
this.adapter = adapter;
this.device = device;
// Create a new listening server socket
try {
tmp = adapter.listenUsingInsecureRfcommWithServiceRecord(device.getName(), UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
} catch (Exception e) {
Log.e(TAG, ".AcceptThread # listen() failed", e);
}
mmServerSocket = tmp;
}
The problem is when I try to do a connect() as I said before
public void run() {
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
Log.d(TAG, "AcceptThread.run: accepting server socket connection");
socket = mmServerSocket.accept(20000);
Log.d(TAG, ".AcceptThread.run # server socket connection accepted");
} catch (Exception e) {
Log.e(TAG, ".run # accept() failed: "+e);
break;
}
}
}
As you can see the ServerSocket accept every incomming connection for 20 seconds or 20000 ms. When the time is up, the app will throw an IOException like this
07-11 10:30:08.355: E/SIMPLECONNECT(1301): .run # accept() failed: java.io.IOException: Connection timed out
which tells me that my device couldnt connect to my android phone. The device doesnt have a connect button on the display, just a send functionalitywhich will send a file to my phone. I believe that this send functionality also do a connect in the background, but I am not sure.
Can anybody pinpoint any solutions for me? I am running my app on a Samsung Galaxy SIII with Android 4.0.4
I finally solved it, the problem is that different Android Versions and different devices seemes to need different sockets. I tryed it with Samsung Galaxy XCOVER, Tab1, Tab2, Nexus, Note, Motorola Defy and HTC Flyer.
The Sockets I used are:
A:
Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
mSocket = (BluetoothSocket) m.invoke(mmDevice, Integer.valueOf(1));
B:
Method m = mmDevice.getClass().getMethod("createInsecureRfcommSocket", new Class[]{int.class});
mSocket=(BluetoothSocket)m.invoke(mmDevice,Integer.valueOf(1));
C:
mSocket=BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mmDevice.getAddress()).createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
Android 4.0.x works for Nexus, Flyer,Tab1 with A,B
Android 4.0.3 works for Tab2 with B
Android 3,6,x works for DEFY with A,B
Android 2.3.6 works for XCOVER with C
I can't find a solution witch works for all devices and I;m not able to find out witch socket will work before I create and use the Socket, especially the XCOVER perform the connect() for all Sockets without throwing an exception, but catch if i try tro write(). So if you want to setup a bloothoh connection wich works for all devices you have to create the sockets, connect an write and then remeber the socket wich works (e.g. in preferences)
I'am use TCP for connect my android phone with Windows 7 PC. When I'am send message phone-PC in LAN this system is work, as i`am use this system in Internet she is down because android app send me "time out". Why?
// The host name can either be a machine name, such as "java.sun.com", or a
// textual representation of its IP address
String host = "10.26.144.118";
int port = 20;
try {
Socket socket = new Socket(InetAddress.getByName(host), port);
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// true for auto flush
writer.println("Hello World");
myView.setText("Send hello world");
} catch (Exception e) {
System.out.println("Error" + e);
myView.setText("Error" + e);
}
You are probably looking for port-forwarding
Your problem is that you mixed up the LAN (local area network) with the WAN (wide area network) aka the internet. Your personal LAN is protected from outside.
You need a static public IP or a DDNS (Dynamic DNS) solution e.g. dyndns. Than you have to forward the traffic from your public IP to you internal Server IP. See also thax's answer.
Than can your smartphone connect to your static public ip or to your DDNS address. Than should your app also work with the mobile network.
I'm experimenting with socket communication between android and windows.
Everything works fine till i use the 10.0.2.2 address which is the loopback to the computer on which the emulator is running. But if i give any other address to the Socket constructor the connection is timing out.
My goal is to communicate between my phone and my computer through the internet.
I also tried it on my phone, so i don't think that it's a firewall problem.
Here is my code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
clientSocket = new Socket("10.0.2.2", 48555);
Log.d("Offdroid", "socket connected");
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.toString());
}
}
public void connectServer(View button) {
try {
String message = "shutdown";
byte[] messageBytes = message.getBytes("US-ASCII");
int messageByteCount = messageBytes.length;
byte[] messageSizeBytes = new byte[2];
messageSizeBytes = intToByteArray(messageByteCount);
byte[] sendBytes = concatenateArrays(messageSizeBytes, messageBytes);
Log.d("Offdroid", Integer.toString(messageSizeBytes.length));
clientSocket.setReceiveBufferSize(16);
clientSocket.setSendBufferSize(512);
OutputStream outStream = clientSocket.getOutputStream();
//InputStream inStream = clientSocket.getInputStream();
outStream.write(sendBytes, 0, sendBytes.length);
} catch(Exception EX) {
Log.e("Offdroid", EX.getMessage());
}
}
I'm also looking for a java built in function instead of the concatenateArrays function which simply put two byte array together.
Edit:
Sorry, maybe i not provided enough information. I have already tried my external ip used for the internet connection and my LAN ip. Port on router is forwarded to my computer. So if i write "192.168.1.101" or the ip given by the internet service provider in place of "10.0.2.2", than i cannot connect.
Edit:
Ok, i figured out it was my firewall.
Emulator takes uses the same network as that of your computer, so it will be able to route it to the computer. But for your phone to connect with your computer, you have to give a different IP, which is basically the IP of the computer.
I am guessing you are using some shared Network, and getting this (10.0.2.2) IP. Your computer should be directly connected to Internet in order for this to work from phone.
Ok, i figured out it was my firewall.
When you use a real Android phone as Internet Remote device, don't you have to set up your WiFi router, connected to your PC (or Android), for Port Forwarding? Then you give your Android Client the PC Server's External IP Address and the Server Port Number. Only then, provided the Port Forwarding works on the router, your remote Android Client (on SIM) can communicate with your PC Server connected to your router.