1

每个人!我正在开发一个 Android 应用程序,允许与附近已安装此应用程序的设备聊天。为了实现这一点,我正在使用 Wi-Fi P2P API 和网络服务发现来搜索附近的此类设备。我已经编写了用于在服务启动的线程中搜索附近设备的代码。当检测到设备时,Service 将其(通过广播意图)发送到显示目前检测到的设备的 Activity。检测到的设备被添加到 recyclerView 中,当用户按下其中一个时,必须建立与此类设备的连接。Wi-Fi Direct 连接建立成功(即 WifiP2pManager.connect() 方法成功)并捕获 WIFI_P2P_CONNECTION_CHANGED_ACTION。在广播接收器中,当这样的广播意图被捕获时,

NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
            if (networkInfo.isConnected()) {

                mManager.requestConnectionInfo(mChannel, connectionInfoListener); }

使用 requestConnectionInfo() 方法,我可以获得有关连接的更多信息,例如我尝试连接的设备的 IP 地址。为了获取此类信息,我为该方法提供了 WifiP2pManager.ConnectionInfoListener 的实现,由 connectionInfoListener 变量表示。这是我实现 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();

“设备”是我的 BroadcastReceiver 实现的一个实例变量,现在并不重要。相反,重要的是 ConnectThread 线程。这是处理连接两个设备之间的套接字所需代码的线程。当我尝试连接到检测到的设备时,ConnectThread 在其 run() 方法中创建一个新的 ChatConnection 实例,将先前获得的 IP 地址和端口号传递给此构造函数:

 public ChatConnection(InetAddress srvAddress, int srvPort, String macAddress) throws IOException {
        ...

        connSocket = new Socket(srvAddress, srvPort);

        ...

    }

这就是问题发生的地方。当我在我的物理设备上测试我的应用程序时,我得到的只是这个异常:

W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 6770): connect failed: ECONNREFUSED (Connection refused)

当然,我也在第二个物理设备上安装了我的应用程序,它被成功检测到并且 Wi-Fi Direct 连接成功建立。但是,当谈到这行代码时:

connSocket = new Socket(srvAddress, srvPort);

抛出异常......我为这个问题的长度道歉,但我想尽可能清楚。我真的提前感谢您的帮助。

编辑:我忘了提及初始化 ServerSocket 的代码。
ServerSocket 在启用 Wi-Fi 后立即启动的线程中进行初始化。
也就是说,当 WifiP2pBroadcastReceiver(扩展 BroadcastReceiver 的应用服务的内部类)捕获 WIFI_P2P_STATE_CHANGED_ACTION 意图时,它会检查 Wi-Fi 是否启用,如果启用,它会启动 ServerSocket 所在的线程:

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();
            }

ServerSocket 在 NsdProviderThread 的 run() 方法中初始化:

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”是 NsdProviderThread 的一个实例变量,声明为 ServerSocket。

4

2 回答 2

0

再次!我终于设法让我的应用程序正常工作。这是我所做的:

  • 硬编码端口号;
  • 在 ConnectionInfoListener 实现中获取组所有者地址时,请确保它是正在使用的设备的 IP 地址。如果不是,则将客户端套接字连接到组所有者地址;否则,让您的应用等待传入连接;
  • 尽快初始化 ServerSocket(例如,在应用启动时)。

为了在建立 Wi-Fi Direct 连接后获取设备的实际 IP 地址,我在“Utils”类中使用了我在这个项目中找到的这个函数(由原始 Android WiFiDirectdemo 派生) :

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”是在 Utils 类中声明的私有静态字符串成本:

private final static String p2pInt = "p2p-p2p0"

但是,在我的应用程序中,我更改了“p2p-wlan0”中的“p2p-p2p0”字符串,因为它看起来像我的 Wi-Fi Direct 设备的网络接口具有该(不同的)名称。我希望这可以帮助任何尝试创建使用 Wi-Fi Direct 连接的应用程序的开发人员。

于 2016-11-16T22:50:05.410 回答
0

看起来您只需要在两端使用正确的端口号。

您使用的是零,这在文档中意味着:

端口号 0 表示端口号是自动分配的,通​​常来自临时端口范围。

因此,当您创建 ServerSocket 时,请确保它正在侦听其他设备用来启动连接的同一端口:

private static final int port = 6770; 

//.....

try {
    server = new ServerSocket(port);
} catch (IOException ex) {
    ex.printStackTrace();
}
于 2016-11-05T03:28:09.413 回答