引言

在HarmonyOS应用开发中,网络代理配置是连接外部服务、访问受限资源的关键技术。华为提供了connection.setAppHttpProxyAPI来设置应用级HTTP代理,但在实际开发中,开发者经常遇到一个棘手问题:设置代理后如何正确取消代理配置?​ 本文将深入探讨这一问题的解决方案,并提供完整的实战代码示例。

问题背景

典型应用场景

  1. 企业应用:员工在公司内网需要通过代理服务器访问外部资源,回家后需要自动切换回直连模式

  2. 安全应用:需要临时通过代理服务器进行安全检测,检测完成后恢复原始网络配置

  3. 测试应用:开发测试阶段需要通过代理抓包分析,正式发布时需要移除代理配置

  4. 跨境应用:用户在不同地区需要切换不同的代理服务器,包括关闭代理

技术挑战

  • connection.setAppHttpProxyAPI只提供了设置代理的方法,没有直接的"取消代理"接口

  • 错误的代理配置会导致应用网络请求全部失败

  • 不同网络环境(Wi-Fi、移动数据)下的代理配置管理复杂

  • 需要确保代理配置的变更不会影响其他应用

核心API详解

connection.setAppHttpProxy

// 设置应用级HTTP代理
connection.setAppHttpProxy(
    host: string,           // 代理服务器主机名
    port: number,           // 代理服务器端口
    exclusionList: Array<string> // 排除列表,这些域名不走代理
): void;

参数说明

  • host:代理服务器地址,如"proxy.example.com"

  • port:代理服务器端口,如8080

  • exclusionList:排除的域名列表,这些域名将直接连接而不通过代理

connection.getDefaultHttpProxy

// 获取默认网络代理配置
connection.getDefaultHttpProxy(callback: AsyncCallback<HttpProxy>): void;
connection.getDefaultHttpProxy(): Promise<HttpProxy>;

// HttpProxy接口定义
interface HttpProxy {
    host: string;      // 代理主机
    port: number;      // 代理端口
    exclusionList: Array<string>; // 排除列表
    pacUrl?: string;   // PAC文件URL(可选)
}

重要特性

  • 如果设置了全局代理,返回全局代理配置

  • 如果进程绑定到特定网络,返回该网络的代理配置

  • 其他情况返回默认网络的代理配置

解决方案一:设置空代理配置

实现原理

通过将代理配置设置为"空值"来达到取消代理的效果。具体来说:

  • host设为空字符串""

  • port设为0

  • exclusionList设为空数组[]

代码实现

import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 方案一:通过设置空代理配置取消代理
 * 适用于完全取消代理,恢复直连模式
 */
class ProxyManager {
    /**
     * 取消应用HTTP代理(方案一)
     */
    static cancelAppHttpProxy(): void {
        try {
            // 设置空代理配置
            connection.setAppHttpProxy('', 0, []);
            
            console.info('代理已成功取消(设置为空配置)');
            
            // 验证代理是否已取消
            this.verifyProxyCancellation();
            
        } catch (error) {
            const err = error as BusinessError;
            console.error(`取消代理失败: ${err.code}, ${err.message}`);
            throw err;
        }
    }
    
    /**
     * 验证代理是否已成功取消
     */
    private static verifyProxyCancellation(): void {
        connection.getDefaultHttpProxy((err, proxyInfo) => {
            if (err) {
                console.error(`获取代理信息失败: ${err.code}, ${err.message}`);
                return;
            }
            
            if (proxyInfo.host === '' && proxyInfo.port === 0) {
                console.info('验证成功:代理配置已清空');
            } else {
                console.warn(`验证警告:代理配置未完全清空,当前配置: ${JSON.stringify(proxyInfo)}`);
            }
        });
    }
    
    /**
     * 设置HTTP代理
     * @param host 代理服务器地址
     * @param port 代理服务器端口
     * @param exclusions 排除的域名列表
     */
    static setHttpProxy(host: string, port: number, exclusions: string[] = []): void {
        try {
            // 参数验证
            if (!host || host.trim() === '') {
                throw new BusinessError({
                    code: 401,
                    message: '代理服务器地址不能为空'
                });
            }
            
            if (port < 1 || port > 65535) {
                throw new BusinessError({
                    code: 402,
                    message: '端口号必须在1-65535范围内'
                });
            }
            
            // 设置代理
            connection.setAppHttpProxy(host, port, exclusions);
            
            console.info(`代理设置成功: ${host}:${port}`);
            console.info(`排除列表: ${exclusions.join(', ') || '无'}`);
            
        } catch (error) {
            const err = error as BusinessError;
            console.error(`设置代理失败: ${err.code}, ${err.message}`);
            throw err;
        }
    }
}

// 使用示例
export class ProxyExample {
    async demonstrateProxyManagement(): Promise<void> {
        try {
            // 1. 设置代理
            console.log('=== 步骤1:设置代理 ===');
            ProxyManager.setHttpProxy('proxy.company.com', 8080, [
                'internal.company.com',
                '*.local'
            ]);
            
            // 等待2秒
            await this.delay(2000);
            
            // 2. 取消代理(方案一)
            console.log('\n=== 步骤2:取消代理(方案一)===');
            ProxyManager.cancelAppHttpProxy();
            
            // 3. 再次设置不同代理
            console.log('\n=== 步骤3:设置新代理 ===');
            ProxyManager.setHttpProxy('backup.proxy.com', 8888);
            
            // 4. 再次取消
            console.log('\n=== 步骤4:再次取消代理 ===');
            ProxyManager.cancelAppHttpProxy();
            
        } catch (error) {
            console.error(`代理管理演示失败: ${error.message}`);
        }
    }
    
    private delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

适用场景

  • 完全取消代理:当应用不再需要任何代理时

  • 网络环境切换:从公司网络切换到家庭网络

  • 临时代理需求结束:安全扫描、测试抓包等临时需求完成后

注意事项

  1. 空字符串验证:必须使用空字符串"",不能使用nullundefined

  2. 端口为0:端口必须设置为0,表示不使用代理端口

  3. 排除列表清空:排除列表必须设置为空数组[]

  4. 立即生效:设置后立即生效,无需重启应用

解决方案二:恢复默认网络配置

实现原理

通过获取当前网络的默认代理配置,然后将应用代理设置为该默认配置。这种方法更灵活,可以:

  • 恢复系统默认代理设置

  • 在不同网络间保持一致的代理策略

  • 避免完全取消代理可能带来的网络访问问题

代码实现

import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 方案二:恢复默认网络配置
 * 适用于需要保持系统代理策略的场景
 */
class DefaultProxyManager {
    private static currentProxyConfig: connection.HttpProxy | null = null;
    
    /**
     * 保存当前代理配置
     */
    static async saveCurrentProxyConfig(): Promise<void> {
        return new Promise((resolve, reject) => {
            connection.getDefaultHttpProxy((err, proxyInfo) => {
                if (err) {
                    console.error(`保存代理配置失败: ${err.code}, ${err.message}`);
                    reject(err);
                    return;
                }
                
                this.currentProxyConfig = proxyInfo;
                console.info('当前代理配置已保存:', JSON.stringify(proxyInfo));
                resolve();
            });
        });
    }
    
    /**
     * 恢复为默认代理配置(方案二)
     */
    static async restoreToDefaultProxy(): Promise<void> {
        try {
            // 获取默认网络代理配置
            const defaultProxy = await this.getDefaultProxy();
            
            // 设置为默认配置
            connection.setAppHttpProxy(
                defaultProxy.host,
                defaultProxy.port,
                defaultProxy.exclusionList
            );
            
            console.info('已恢复为默认代理配置:', JSON.stringify(defaultProxy));
            
            // 验证恢复结果
            await this.verifyRestoration(defaultProxy);
            
        } catch (error) {
            const err = error as BusinessError;
            console.error(`恢复默认代理失败: ${err.code}, ${err.message}`);
            throw err;
        }
    }
    
    /**
     * 获取默认代理配置
     */
    private static getDefaultProxy(): Promise<connection.HttpProxy> {
        return new Promise((resolve, reject) => {
            connection.getDefaultHttpProxy((err, proxyInfo) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(proxyInfo);
            });
        });
    }
    
    /**
     * 验证代理恢复结果
     */
    private static async verifyRestoration(expectedProxy: connection.HttpProxy): Promise<void> {
        const currentProxy = await this.getDefaultProxy();
        
        if (currentProxy.host === expectedProxy.host &&
            currentProxy.port === expectedProxy.port &&
            JSON.stringify(currentProxy.exclusionList) === JSON.stringify(expectedProxy.exclusionList)) {
            console.info('验证成功:代理配置已恢复为默认值');
        } else {
            console.warn(`验证警告:代理配置恢复不一致
                期望: ${JSON.stringify(expectedProxy)}
                实际: ${JSON.stringify(currentProxy)}`);
        }
    }
    
    /**
     * 智能代理管理:根据网络环境自动调整
     */
    static async smartProxyManagement(): Promise<void> {
        try {
            // 获取当前网络类型
            const netHandle = connection.getDefaultNet();
            const netCapabilities = await this.getNetCapabilities(netHandle);
            
            // 根据网络类型决定代理策略
            if (netCapabilities.hasTransport(connection.NetCapabilities.TRANSPORT_WIFI)) {
                console.log('检测到Wi-Fi网络,检查代理配置...');
                
                // 获取Wi-Fi网络的默认代理
                const wifiProxy = await this.getDefaultProxy();
                
                if (wifiProxy.host && wifiProxy.port > 0) {
                    console.warn('检测到Wi-Fi网络已配置代理,应用将使用系统代理设置');
                    // 保持系统代理设置
                } else {
                    console.log('Wi-Fi网络未配置代理,应用使用直连模式');
                    // 取消代理
                    connection.setAppHttpProxy('', 0, []);
                }
                
            } else if (netCapabilities.hasTransport(connection.NetCapabilities.TRANSPORT_CELLULAR)) {
                console.log('检测到移动数据网络,建议取消代理以节省流量');
                // 移动数据下取消代理
                connection.setAppHttpProxy('', 0, []);
                
            } else {
                console.log('未知网络类型,使用默认代理策略');
                await this.restoreToDefaultProxy();
            }
            
        } catch (error) {
            console.error(`智能代理管理失败: ${error.message}`);
            // 失败时恢复默认配置
            await this.restoreToDefaultProxy();
        }
    }
    
    /**
     * 获取网络能力
     */
    private static getNetCapabilities(netHandle: connection.NetHandle): Promise<connection.NetCapabilities> {
        return new Promise((resolve, reject) => {
            connection.getNetCapabilities(netHandle, (err, capabilities) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(capabilities);
            });
        });
    }
}

// 使用示例
export class DefaultProxyExample {
    async demonstrateDefaultProxyRestoration(): Promise<void> {
        try {
            // 1. 保存当前代理配置
            console.log('=== 步骤1:保存当前代理配置 ===');
            await DefaultProxyManager.saveCurrentProxyConfig();
            
            // 2. 设置自定义代理
            console.log('\n=== 步骤2:设置自定义代理 ===');
            connection.setAppHttpProxy('custom.proxy.com', 9090, ['api.local']);
            
            // 等待用户操作
            console.log('自定义代理已设置,等待3秒...');
            await this.delay(3000);
            
            // 3. 恢复为默认配置
            console.log('\n=== 步骤3:恢复为默认代理配置 ===');
            await DefaultProxyManager.restoreToDefaultProxy();
            
            // 4. 演示智能代理管理
            console.log('\n=== 步骤4:智能代理管理演示 ===');
            await DefaultProxyManager.smartProxyManagement();
            
        } catch (error) {
            console.error(`默认代理恢复演示失败: ${error.message}`);
        }
    }
    
    private delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

适用场景

  • 企业环境:恢复公司网络的默认代理设置

  • 多网络切换:在不同网络环境间保持正确的代理策略

  • 系统集成:需要与系统代理设置保持一致的应用

  • 故障恢复:当自定义代理出现问题时恢复系统默认

注意事项

  1. 异步操作getDefaultHttpProxy是异步方法,需要使用回调或Promise

  2. 网络绑定:如果应用绑定到特定网络,获取的是该网络的代理配置

  3. 配置一致性:确保恢复的配置与系统当前网络环境匹配

  4. 错误处理:网络不可用时需要适当的错误处理

完整实战示例

场景:企业VPN应用代理管理

下面是一个完整的企业VPN应用代理管理示例,结合了两种方案的优势:

import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';

/**
 * 企业VPN代理管理器
 * 支持多种代理模式和智能切换
 */
export class EnterpriseProxyManager {
    private proxyConfigs: Map<string, ProxyConfig> = new Map();
    private currentMode: ProxyMode = ProxyMode.DIRECT;
    
    // 代理模式枚举
    static readonly ProxyMode = {
        DIRECT: 'direct',      // 直连模式
        SYSTEM: 'system',      // 系统代理
        CUSTOM: 'custom',      // 自定义代理
        VPN: 'vpn'            // VPN代理
    };
    
    constructor() {
        this.initializeDefaultConfigs();
    }
    
    /**
     * 初始化默认代理配置
     */
    private initializeDefaultConfigs(): void {
        // 公司内网代理
        this.proxyConfigs.set('company', {
            name: '公司内网代理',
            host: 'proxy.company.com',
            port: 8080,
            exclusionList: [
                '*.company.local',
                'internal.api.com',
                '192.168.*'
            ],
            description: '公司内部网络代理,用于访问外部资源'
        });
        
        // 开发测试代理
        this.proxyConfigs.set('development', {
            name: '开发测试代理',
            host: 'dev.proxy.com',
            port: 8888,
            exclusionList: [
                'localhost',
                '127.0.0.1',
                '*.test'
            ],
            description: '开发环境代理,用于调试和抓包'
        });
        
        // VPN代理
        this.proxyConfigs.set('vpn', {
            name: 'VPN安全代理',
            host: 'vpn.secure.com',
            port: 443,
            exclusionList: [],
            description: 'VPN加密通道,用于安全访问'
        });
    }
    
    /**
     * 切换代理模式
     */
    async switchProxyMode(mode: string, configKey?: string): Promise<boolean> {
        try {
            console.log(`切换代理模式: ${mode}`);
            
            switch (mode) {
                case EnterpriseProxyManager.ProxyMode.DIRECT:
                    // 方案一:直连模式(取消代理)
                    return await this.setDirectMode();
                    
                case EnterpriseProxyManager.ProxyMode.SYSTEM:
                    // 方案二:系统代理模式
                    return await this.setSystemMode();
                    
                case EnterpriseProxyManager.ProxyMode.CUSTOM:
                    // 自定义代理模式
                    if (!configKey) {
                        throw new BusinessError({
                            code: 403,
                            message: '自定义模式需要指定配置键'
                        });
                    }
                    return await this.setCustomMode(configKey);
                    
                case EnterpriseProxyManager.ProxyMode.VPN:
                    // VPN代理模式
                    return await this.setVpnMode();
                    
                default:
                    throw new BusinessError({
                        code: 404,
                        message: `不支持的代理模式: ${mode}`
                    });
            }
            
        } catch (error) {
            console.error(`切换代理模式失败: ${error.message}`);
            return false;
        }
    }
    
    /**
     * 设置直连模式(取消代理)
     */
    private async setDirectMode(): Promise<boolean> {
        try {
            // 方案一:设置空代理配置
            connection.setAppHttpProxy('', 0, []);
            
            this.currentMode = EnterpriseProxyManager.ProxyMode.DIRECT;
            console.info('已切换到直连模式(代理已取消)');
            
            // 验证模式切换
            await this.verifyCurrentMode();
            
            return true;
            
        } catch (error) {
            console.error(`设置直连模式失败: ${error.message}`);
            return false;
        }
    }
    
    /**
     * 设置系统代理模式
     */
    private async setSystemMode(): Promise<boolean> {
        try {
            // 获取系统默认代理配置
            const systemProxy = await this.getSystemProxy();
            
            // 设置为系统配置
            connection.setAppHttpProxy(
                systemProxy.host,
                systemProxy.port,
                systemProxy.exclusionList
            );
            
            this.currentMode = EnterpriseProxyManager.ProxyMode.SYSTEM;
            console.info('已切换到系统代理模式:', JSON.stringify(systemProxy));
            
            await this.verifyCurrentMode();
            return true;
            
        } catch (error) {
            console.error(`设置系统代理模式失败: ${error.message}`);
            return false;
        }
    }
    
    /**
     * 设置自定义代理模式
     */
    private async setCustomMode(configKey: string): Promise<boolean> {
        const config = this.proxyConfigs.get(configKey);
        if (!config) {
            throw new BusinessError({
                code: 405,
                message: `未找到代理配置: ${configKey}`
            });
        }
        
        try {
            // 设置自定义代理
            connection.setAppHttpProxy(
                config.host,
                config.port,
                config.exclusionList
            );
            
            this.currentMode = EnterpriseProxyManager.ProxyMode.CUSTOM;
            console.info(`已切换到自定义代理模式 [${config.name}]:`, {
                host: config.host,
                port: config.port,
                exclusions: config.exclusionList.length
            });
            
            await this.verifyCurrentMode();
            return true;
            
        } catch (error) {
            console.error(`设置自定义代理模式失败: ${error.message}`);
            return false;
        }
    }
    
    /**
     * 设置VPN代理模式
     */
    private async setVpnMode(): Promise<boolean> {
        // 检查VPN连接状态
        const vpnConnected = await this.checkVpnConnection();
        if (!vpnConnected) {
            const userConfirmed = await this.showVpnPrompt();
            if (!userConfirmed) {
                console.warn('用户取消VPN连接,保持当前模式');
                return false;
            }
        }
        
        return await this.setCustomMode('vpn');
    }
    
    /**
     * 获取系统代理配置
     */
    private getSystemProxy(): Promise<connection.HttpProxy> {
        return new Promise((resolve, reject) => {
            connection.getDefaultHttpProxy((err, proxyInfo) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(proxyInfo);
            });
        });
    }
    
    /**
     * 验证当前代理模式
     */
    private async verifyCurrentMode(): Promise<void> {
        try {
            const currentProxy = await this.getSystemProxy();
            
            console.log('当前代理配置验证:');
            console.log(`- 主机: ${currentProxy.host || '(空)'}`);
            console.log(`- 端口: ${currentProxy.port || 0}`);
            console.log(`- 排除域名: ${currentProxy.exclusionList?.length || 0}个`);
            console.log(`- 当前模式: ${this.currentMode}`);
            
        } catch (error) {
            console.warn(`代理验证失败: ${error.message}`);
        }
    }
    
    /**
     * 检查VPN连接状态
     */
    private async checkVpnConnection(): Promise<boolean> {
        // 这里应该实现实际的VPN连接检查
        // 简化实现,返回模拟值
        return Math.random() > 0.5;
    }
    
    /**
     * 显示VPN连接提示
     */
    private async showVpnPrompt(): Promise<boolean> {
        return new Promise((resolve) => {
            promptAction.showToast({
                message: '检测到VPN未连接,是否立即连接?',
                duration: 3000,
                bottom: '100vp'
            });
            
            // 简化实现,实际应该显示对话框让用户选择
            setTimeout(() => {
                resolve(true);
            }, 1000);
        });
    }
    
    /**
     * 获取当前代理状态
     */
    getCurrentStatus(): ProxyStatus {
        return {
            mode: this.currentMode,
            configs: Array.from(this.proxyConfigs.keys()),
            timestamp: new Date().toISOString()
        };
    }
    
    /**
     * 添加自定义代理配置
     */
    addProxyConfig(key: string, config: ProxyConfig): void {
        if (this.proxyConfigs.has(key)) {
            console.warn(`代理配置 ${key} 已存在,将被覆盖`);
        }
        
        this.proxyConfigs.set(key, config);
        console.info(`代理配置已添加: ${key} - ${config.name}`);
    }
    
    /**
     * 移除代理配置
     */
    removeProxyConfig(key: string): boolean {
        const removed = this.proxyConfigs.delete(key);
        if (removed) {
            console.info(`代理配置已移除: ${key}`);
        } else {
            console.warn(`代理配置不存在: ${key}`);
        }
        return removed;
    }
}

// 类型定义
interface ProxyConfig {
    name: string;
    host: string;
    port: number;
    exclusionList: string[];
    description?: string;
}

interface ProxyStatus {
    mode: string;
    configs: string[];
    timestamp: string;
}

type ProxyMode = 'direct' | 'system' | 'custom' | 'vpn';

// 使用示例
export class EnterpriseProxyExample {
    private proxyManager: EnterpriseProxyManager;
    
    constructor() {
        this.proxyManager = new EnterpriseProxyManager();
    }
    
    async runCompleteDemo(): Promise<void> {
        console.log('=== 企业代理管理器完整演示 ===\n');
        
        try {
            // 1. 显示初始状态
            console.log('1. 初始状态:');
            console.log(JSON.stringify(this.proxyManager.getCurrentStatus(), null, 2));
            
            // 2. 切换到公司代理
            console.log('\n2. 切换到公司代理模式:');
            const success1 = await this.proxyManager.switchProxyMode(
                EnterpriseProxyManager.ProxyMode.CUSTOM,
                'company'
            );
            console.log(`切换结果: ${success1 ? '成功' : '失败'}`);
            
            await this.delay(2000);
            
            // 3. 切换到直连模式(取消代理)
            console.log('\n3. 切换到直连模式(取消代理):');
            const success2 = await this.proxyManager.switchProxyMode(
                EnterpriseProxyManager.ProxyMode.DIRECT
            );
            console.log(`切换结果: ${success2 ? '成功' : '失败'}`);
            
            await this.delay(2000);
            
            // 4. 切换到系统代理模式
            console.log('\n4. 切换到系统代理模式:');
            const success3 = await this.proxyManager.switchProxyMode(
                EnterpriseProxyManager.ProxyMode.SYSTEM
            );
            console.log(`切换结果: ${success3 ? '成功' : '失败'}`);
            
            await this.delay(2000);
            
            // 5. 添加新的代理配置
            console.log('\n5. 添加新的代理配置:');
            this.proxyManager.addProxyConfig('test', {
                name: '测试环境代理',
                host: 'test.proxy.com',
                port: 9999,
                exclusionList: ['*.test.local'],
                description: '测试环境专用代理'
            });
            
            // 6. 切换到新配置
            console.log('\n6. 切换到新配置:');
            const success4 = await this.proxyManager.switchProxyMode(
                EnterpriseProxyManager.ProxyMode.CUSTOM,
                'test'
            );
            console.log(`切换结果: ${success4 ? '成功' : '失败'}`);
            
            // 7. 显示最终状态
            console.log('\n7. 最终状态:');
            console.log(JSON.stringify(this.proxyManager.getCurrentStatus(), null, 2));
            
            console.log('\n=== 演示完成 ===');
            
        } catch (error) {
            console.error(`演示过程中出错: ${error.message}`);
        }
    }
    
    private delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

常见问题解答(FAQ)

Q1:如何获取设备当前的代理列表?

A:通过connection.getDefaultHttpProxyAPI可以获取当前网络的代理配置信息。如果需要获取所有网络的代理配置,需要结合connection.getAllNets遍历所有网络接口。

async function getAllNetworkProxies(): Promise<Map<string, connection.HttpProxy>> {
    const proxyMap = new Map<string, connection.HttpProxy>();
    
    try {
        // 获取所有网络
        const nets = connection.getAllNets();
        
        for (const net of nets) {
            const proxy = await getProxyForNet(net);
            proxyMap.set(net.netId.toString(), proxy);
        }
        
    } catch (error) {
        console.error(`获取网络代理失败: ${error.message}`);
    }
    
    return proxyMap;
}

function getProxyForNet(netHandle: connection.NetHandle): Promise<connection.HttpProxy> {
    return new Promise((resolve, reject) => {
        connection.getDefaultHttpProxy(netHandle, (err, proxyInfo) => {
            if (err) {
                reject(err);
                return;
            }
            resolve(proxyInfo);
        });
    });
}

Q2:如何检测Wi-Fi代理并阻止?

A:可以通过检测当前网络类型和代理配置来实现智能阻止:

class ProxyBlocker {
    /**
     * 检测并阻止Wi-Fi代理
     */
    static async detectAndBlockWifiProxy(): Promise<boolean> {
        try {
            // 获取当前网络
            const netHandle = connection.getDefaultNet();
            const capabilities = await this.getNetCapabilities(netHandle);
            
            // 检查是否为Wi-Fi网络
            if (!capabilities.hasTransport(connection.NetCapabilities.TRANSPORT_WIFI)) {
                console.log('当前不是Wi-Fi网络,无需检测代理');
                return false;
            }
            
            // 获取代理配置
            const proxyInfo = await this.getProxyInfo();
            
            // 检查是否设置了代理
            if (proxyInfo.host && proxyInfo.port > 0) {
                console.warn('检测到Wi-Fi网络已配置代理');
                
                // 显示警告并询问用户
                const userChoice = await this.showProxyWarning(proxyInfo);
                
                if (userChoice === 'block') {
                    // 用户选择阻止,取消代理
                    connection.setAppHttpProxy('', 0, []);
                    console.info('已阻止Wi-Fi代理');
                    return true;
                } else if (userChoice === 'allow') {
                    // 用户允许使用代理
                    console.info('用户允许使用代理');
                    return false;
                } else {
                    // 用户取消,保持原样
                    console.info('用户取消操作');
                    return false;
                }
            }
            
            return false;
            
        } catch (error) {
            console.error(`代理检测失败: ${error.message}`);
            return false;
        }
    }
    
    /**
     * 显示代理警告
     */
    private static async showProxyWarning(proxyInfo: connection.HttpProxy): Promise<'block' | 'allow' | 'cancel'> {
        // 实际应用中应该显示对话框让用户选择
        // 这里简化为控制台提示
        console.warn(`
⚠️ 安全警告:检测到Wi-Fi代理配置
代理服务器: ${proxyInfo.host}:${proxyInfo.port}
排除域名: ${proxyInfo.exclusionList?.join(', ') || '无'}

请确认是否信任此代理服务器:
1. 阻止代理(推荐)
2. 允许使用代理
3. 取消操作
`);
        
        // 模拟用户选择(实际应该等待用户输入)
        return 'block';
    }
}

Q3:为何无论connection.setAppHttpProxy设置的域名是否正确,都会返回成功?

Aconnection.setAppHttpProxy接口仅负责将代理配置写入系统,不会验证代理服务器的可用性。这是设计上的考虑:

  1. 性能考虑:验证代理可用性需要网络请求,会增加延迟

  2. 灵活性:允许设置暂时不可用的代理(如服务器维护中)

  3. 异步验证:应用可以在设置后自行验证代理可用性

建议的验证方案

class ProxyValidator {
    /**
     * 验证代理服务器可用性
     */
    static async validateProxy(host: string, port: number, timeout: number = 5000): Promise<boolean> {
        return new Promise((resolve) => {
            // 创建TCP连接测试
            const socket = new net.Socket();
            let isConnected = false;
            
            // 设置超时
            const timer = setTimeout(() => {
                socket.destroy();
                resolve(false);
            }, timeout);
            
            socket.on('connect', () => {
                clearTimeout(timer);
                isConnected = true;
                socket.destroy();
                resolve(true);
            });
            
            socket.on('error', (error) => {
                clearTimeout(timer);
                console.warn(`代理连接失败: ${error.message}`);
                resolve(false);
            });
            
            socket.on('timeout', () => {
                clearTimeout(timer);
                socket.destroy();
                resolve(false);
            });
            
            // 尝试连接
            socket.connect(port, host);
        });
    }
    
    /**
     * 设置并验证代理
     */
    static async setAndValidateProxy(host: string, port: number, exclusions: string[] = []): Promise<boolean> {
        try {
            // 1. 设置代理
            connection.setAppHttpProxy(host, port, exclusions);
            console.info(`代理已设置: ${host}:${port}`);
            
            // 2. 验证代理可用性
            console.info('正在验证代理可用性...');
            const isValid = await this.validateProxy(host, port);
            
            if (isValid) {
                console.info('代理验证成功,服务器可用');
                return true;
            } else {
                console.warn('代理验证失败,服务器可能不可用');
                
                // 可选:自动取消无效代理
                // connection.setAppHttpProxy('', 0, []);
                // console.info('已自动取消无效代理');
                
                return false;
            }
            
        } catch (error) {
            console.error(`设置验证代理失败: ${error.message}`);
            return false;
        }
    }
}

最佳实践建议

1. 代理配置管理策略

分级管理

// 代理配置级别
enum ProxyPriority {
    CRITICAL = 1,    // 关键业务,必须使用代理
    IMPORTANT = 2,   // 重要业务,建议使用代理
    NORMAL = 3,      // 普通业务,根据网络决定
    OPTIONAL = 4     // 可选业务,不使用代理
}

// 根据业务优先级决定代理策略
function determineProxyStrategy(priority: ProxyPriority, networkType: string): boolean {
    const strategies = {
        [ProxyPriority.CRITICAL]: {
            wifi: true,      // Wi-Fi下使用代理
            cellular: true,  // 移动数据下使用代理
            ethernet: true   // 有线网络下使用代理
        },
        [ProxyPriority.IMPORTANT]: {
            wifi: true,
            cellular: false, // 移动数据下不使用代理(节省流量)
            ethernet: true
        },
        [ProxyPriority.NORMAL]: {
            wifi: false,     // 仅在公司网络使用代理
            cellular: false,
            ethernet: false
        },
        [ProxyPriority.OPTIONAL]: {
            wifi: false,
            cellular: false,
            ethernet: false
        }
    };
    
    return strategies[priority][networkType] || false;
}

2. 错误处理与重试机制

class RobustProxyManager {
    private maxRetries = 3;
    private retryDelay = 1000; // 1秒
    
    /**
     * 带重试的代理设置
     */
    async setProxyWithRetry(host: string, port: number, exclusions: string[] = []): Promise<boolean> {
        let retryCount = 0;
        
        while (retryCount < this.maxRetries) {
            try {
                console.log(`尝试设置代理 (第${retryCount + 1}次): ${host}:${port}`);
                
                // 设置代理
                connection.setAppHttpProxy(host, port, exclusions);
                
                // 验证设置是否成功
                const isSet = await this.verifyProxySetting(host, port);
                
                if (isSet) {
                    console.info(`代理设置成功: ${host}:${port}`);
                    return true;
                } else {
                    throw new Error('代理设置验证失败');
                }
                
            } catch (error) {
                retryCount++;
                console.warn(`代理设置失败 (第${retryCount}次): ${error.message}`);
                
                if (retryCount < this.maxRetries) {
                    console.log(`等待${this.retryDelay}ms后重试...`);
                    await this.delay(this.retryDelay);
                    // 指数退避
                    this.retryDelay *= 2;
                }
            }
        }
        
        console.error(`代理设置失败,已达到最大重试次数: ${this.maxRetries}`);
        return false;
    }
    
    /**
     * 验证代理设置
     */
    private async verifyProxySetting(expectedHost: string, expectedPort: number): Promise<boolean> {
        return new Promise((resolve) => {
            connection.getDefaultHttpProxy((err, proxyInfo) => {
                if (err) {
                    resolve(false);
                    return;
                }
                
                const isCorrect = proxyInfo.host === expectedHost && 
                                 proxyInfo.port === expectedPort;
                resolve(isCorrect);
            });
        });
    }
}

3. 性能优化建议

  1. 延迟加载:代理配置在需要时才加载,避免启动时立即设置

  2. 缓存机制:缓存常用的代理配置,减少重复查询

  3. 批量操作:多个代理变更合并执行,减少系统调用

  4. 异步验证:代理验证在后台线程执行,不阻塞主线程

4. 安全注意事项

  1. 敏感信息保护:代理密码等敏感信息应加密存储

  2. 配置验证:用户输入的代理配置需要严格验证

  3. 权限控制:代理设置功能需要适当的用户权限

  4. 日志记录:记录代理变更操作,便于审计和故障排查

总结

通过本文的详细讲解,我们深入探讨了HarmonyOS 6中connection.setAppHttpProxy设置代理后如何取消代理的两种解决方案:

核心解决方案对比

方案

方法

适用场景

优点

缺点

方案一

设置空代理配置

完全取消代理,恢复直连

简单直接,立即生效

可能丢失系统代理配置

方案二

恢复默认配置

保持系统代理策略

更灵活,保持一致性

需要获取当前网络配置

关键要点

  1. 方案选择:根据具体业务需求选择合适的取消代理方案

  2. 错误处理:完善的错误处理机制确保代理配置的可靠性

  3. 网络感知:结合网络状态智能管理代理配置

  4. 用户体验:提供清晰的代理状态反馈和操作确认

扩展思考

在实际开发中,还可以考虑以下扩展功能:

  1. 代理自动发现:实现WPAD协议自动发现代理配置

  2. 代理负载均衡:多个代理服务器之间的智能切换

  3. 代理健康检查:定期检查代理服务器可用性

  4. 代理规则引擎:基于URL、内容类型等的智能代理规则

通过合理使用HarmonyOS的网络代理API,开发者可以构建出既安全又高效的企业级网络应用,满足各种复杂的网络环境需求。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐