You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移动数据开启时Android通过WiFi静态IP建立Socket连接异常问题

Android Socket连接问题:同时连WiFi和移动数据时路由错误

我开发一款Android应用,需要通过Socket连接至服务器。仅连接托管该服务器的特定WiFi网络时,连接可正常实现;但同时连接该WiFi网络与移动数据网络时,Android会尝试通过移动网络建立Socket连接,进而抛出Socket异常。以下是仅在WiFi网络连接(移动数据关闭)状态下可正常运行的代码:

static class StartTCPconnection extends AsyncTask<Void, Void, Void> { 
    final WeakReference<RemoteActivity> activity; 
    StartTCPconnection(WeakReference<RemoteActivity> activity) { 
        this.activity = activity; 
    } 
    @Override 
    protected Void doInBackground(Void... voids) { 
        try { 
            socket = new Socket("192.168.4.1", 900); 
            Log.d(TAG, "is socket connected ? ...." + socket.isConnected()); 
            printWriter = new PrintWriter(socket.getOutputStream(), true); 
            Log.i(TAG, "Checking if socket is really connected " + (socket.getLocalSocketAddress())); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return null; 
    } 
    @Override 
    protected void onPostExecute(Void aVoid) { 
        super.onPostExecute(aVoid); 
        if (socket != null) { 
            if (socket.isConnected() && isWifi) { 
                Log.d(TAG, "onPostExecute: " + socket.isConnected()); 
                Toast.makeText(activity.get(), "Connection established", Toast.LENGTH_SHORT).show(); 
                Log.e(TAG, "onPostExecute: " + activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 99)); 
                if (activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 1) == 1) { 
                    activity.get().joyStickFragment.checkSocketInstance(socket); 
                    activity.get().joyStickFragment.changeUIForConnect(); 
                } else if (activity.get().getSharedPreferences(Constants.REMOTE_SWITCH_SHARED_PREFERENCE, Context.MODE_PRIVATE).getInt(Constants.REMOTE_SWITCH_KEY, 1) == 2) { 
                    Log.e(TAG, "onPostExecute:Check " + socket.isConnected()); 
                    activity.get().buttonRemoteFragment.checkSocketInstance(socket); 
                    activity.get().buttonRemoteFragment.changeUIForConnect(); 
                } 
                activity.get().connectionIndicatorImage.setImageResource(R.drawable.avishkaar_logo_on); 
                activity.get().wifiIndicator.setImageResource(R.drawable.wifi_connected_icon); 
            } 
        } else { 
            // Toast.makeText(activity.get(), "Wrong Wifi Network connected", Toast.LENGTH_SHORT).show(); 
        } 
    } 
}

这个问题我之前帮同事排查过,核心原因是Android的默认网络路由策略:当设备同时连接WiFi和移动数据时,部分厂商的定制系统会优先选择移动数据作为默认网络,导致你的Socket尝试通过移动网络去连接内网IP 192.168.4.1——这显然行不通,所以才会抛出异常。

下面是直接可落地的解决方案,修改你的代码就能搞定:

解决方案:强制Socket使用目标WiFi网络

我们需要让Socket明确绑定到你指定的WiFi网络接口,这样不管移动数据是否开启,Socket都会走WiFi连接服务器。

1. 先添加必要权限

在AndroidManifest.xml里加上这些权限,不然没法获取和操作网络状态:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

2. 修改你的AsyncTask代码

我调整了doInBackground方法,加入了寻找目标WiFi并绑定Socket的逻辑:

static class StartTCPconnection extends AsyncTask<Void, Void, Void> { 
    final WeakReference<RemoteActivity> activity; 
    // 替换成你实际要连接的WiFi名称
    private static final String TARGET_WIFI_SSID = "你的目标WiFiSSID"; 

    StartTCPconnection(WeakReference<RemoteActivity> activity) { 
        this.activity = activity; 
    } 

    @Override 
    protected Void doInBackground(Void... voids) { 
        Socket tempSocket = null;
        PrintWriter tempPrintWriter = null;
        RemoteActivity currentActivity = activity.get();

        if (currentActivity == null) {
            return null;
        }

        // 第一步:找到当前连接的目标WiFi网络
        ConnectivityManager connectivityManager = (ConnectivityManager) currentActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
        Network targetNetwork = null;

        if (connectivityManager != null) {
            // 获取当前所有活跃网络
            Network[] allNetworks = connectivityManager.getAllNetworks();
            for (Network network : allNetworks) {
                NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
                if (networkInfo != null 
                    && networkInfo.getType() == ConnectivityManager.TYPE_WIFI 
                    && networkInfo.isConnected()) {
                    // 获取当前WiFi的SSID
                    WifiManager wifiManager = (WifiManager) currentActivity.getSystemService(Context.WIFI_SERVICE);
                    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                    // 注意:SSID会被双引号包裹,要先去掉
                    String currentSsid = wifiInfo.getSSID().replace("\"", "");
                    // 匹配目标WiFi
                    if (TARGET_WIFI_SSID.equals(currentSsid)) {
                        targetNetwork = network;
                        break;
                    }
                }
            }
        }

        try { 
            if (targetNetwork != null) {
                // 第二步:创建Socket并绑定到目标WiFi网络
                tempSocket = new Socket();
                // 设置5秒超时,避免卡住
                tempSocket.connect(new InetSocketAddress("192.168.4.1", 900), 5000);
                // 关键:将Socket绑定到目标WiFi网络
                targetNetwork.bindSocket(tempSocket);
            } else {
                // 如果没找到目标WiFi,尝试普通连接(或者提示用户)
                tempSocket = new Socket("192.168.4.1", 900);
            }

            Log.d(TAG, "is socket connected ? ...." + tempSocket.isConnected()); 
            tempPrintWriter = new PrintWriter(tempSocket.getOutputStream(), true); 
            Log.i(TAG, "Checking if socket is really connected " + (tempSocket.getLocalSocketAddress())); 

            // 赋值给全局变量
            socket = tempSocket;
            printWriter = tempPrintWriter;

        } catch (IOException e) { 
            e.printStackTrace(); 
            // 连接失败时提示用户
            if (currentActivity != null) {
                currentActivity.runOnUiThread(() -> 
                    Toast.makeText(currentActivity, "连接失败,请确认已连接目标WiFi", Toast.LENGTH_SHORT).show()
                );
            }
        } 
        return null; 
    } 

    // 原onPostExecute方法保持不变,这里就不重复粘贴了
}

3. 备用方案:绑定整个进程到WiFi

如果上面的bindSocket方法在某些机型上不生效,你可以试试把整个应用进程绑定到目标WiFi网络,这样所有网络请求都会走这个WiFi:

在找到targetNetwork后,添加这行代码:

// 绑定进程到目标WiFi网络
connectivityManager.bindProcessToNetwork(targetNetwork);

注意:用完之后记得调用connectivityManager.bindProcessToNetwork(null)解绑,不然切换网络时会有问题。

一些注意点

  • SSID匹配:一定要记得去掉SSID的双引号,不然匹配不上。
  • 版本兼容:bindSocket和bindProcessToNetwork都是API 21(Android 5.0)及以上支持的,如果要兼容更低版本,可能需要用反射或者其他方式。
  • 网络状态检查:建议在启动AsyncTask前先检查是否已连接到目标WiFi,避免不必要的连接尝试。

内容的提问来源于stack exchange,提问作者Faizan Mir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:12:48