1

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.

Nehil
  • 11
  • 1
  • 5

2 Answers2

0

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();
}
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • I tried to hard-code the port number in the ServerSocket like this: server = new ServerSocket(6770); And I did the same thing in the constructor of ChatConnection: connSocket = new Socket(srvAddress, 6770); Now when the app comes to that line of code, the exception isn't thrown anymore, but the app just hangs there (no errors, no exceptions) like waiting forever for the connection to be established... – Nehil Nov 05 '16 at 18:27
0

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.

Nehil
  • 11
  • 1
  • 5