庖丁解牛之仿《闪传》实现文件传输(上)

1.> 转载请标明出处

本文出自[HCY的微博]

一、概述

相信不少人有接触过《闪传》、《快牙》这两款传输分享工具,它们无需WIFI,3G和SIM卡,不费任何流量,极速传输内容,每秒大约2M的速率。方便了多台手机之间的数据传输,直接秒蓝牙好几条街。今天我就来剖析一下他们的核心技术实现。

二、实现思路

接收方:

  1. 建立WIFI热点,如SSID为QuickShareMI3。
  2. 通过ServerSocket在指定端口建立Socket服务端。
  3. 通过UDP广播,将第2步中建立的服务端的ip地址和端口广播出去。

发送方:

  1. 开启WIFI扫描功能,当扫描到SSID为QuickShareMI3的热点时,连接至该热点。
  2. 监听UDP广播,获取接收方广播的ip地址和端口号。
  3. 获取到ip地址和端口号后,连接到服务端,至此,传输链路建立成功,可以开始收发文件了。

三、WIFI模块的功能实现

  • 创建WIFI热点

    创建WIFI热点时需要配置热点信息,如下代码实现了无密码、WPA_PSK、WPA2_PSK三种类型热点的信息配置:

    /**
     * 创建wifi热点配置信息,详见系统Setting App的WifiApDialog.java源码,当wifiApType为
     * {@link WifiApType#TYPE_NONE}时,ssid和psw不起作用。另外在SDK_INT<14的系统版本下,使用
     * {@link WifiApType#TYPE_WPA2_PSK}无效
     * 
     * @param wifiApType
     *            热点类型,{eg:{@link WifiApType#TYPE_NONE}
     * @param ssid
     * 
     * @param password
     *            热点密码 ,密码可以为8-63个ASCII码或8-64个十六进制的字符
     * @return
     */
    public WifiConfiguration createWifiApConfiguration(int wifiApType,
            String ssid, String password) {
        if (TextUtils.isEmpty(ssid)) {
            throw new RuntimeException("ssid should not be null");
        }
        WifiConfiguration config = new WifiConfiguration();
        config.SSID = ssid;
        switch (wifiApType) {
        case WifiApType.TYPE_NONE:
            config.allowedKeyManagement.set(KeyMgmt.NONE);
            break;
        case WifiApType.TYPE_WPA_PSK:
        case WifiApType.TYPE_WPA2_PSK:
            if (TextUtils.isEmpty(password) || password.length() < 8) {
                throw new RuntimeException("密码至少为8个字符");
            }
            config.preSharedKey = password;
            final int WPA2_PSK = 4;
            config.allowedKeyManagement
                    .set(wifiApType == WifiApType.TYPE_WPA_PSK ? KeyMgmt.WPA_PSK
                            : WPA2_PSK);
            config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
            break;
        }
        return config;
    }

配置完上述热点信息之后,建立热点

    /**
     * 打开wifi热点
     * 
     * @param wifiConfig
     * @return
     */
    public boolean openWifiAp(WifiConfiguration wifiConfig) {
        // 热点与wifi不能共存,开启热点需要关闭wifi
        wifiManager.setWifiEnabled(false);
        return setWifiApEnabled(wifiConfig, true);
    }
/**
     * 设置wifi热点的激活状态,反射实现
     * 
     * @param wifiConfig
     *            配置信息
     * @param enabled
     *            是否激活
     */
    public boolean setWifiApEnabled(WifiConfiguration wifiConfig,
            boolean enabled) {
        try {
            Method setWifiApEnabled = WifiManager.class.getMethod(
                    "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
            setWifiApEnabled.setAccessible(true);
            return (Boolean) setWifiApEnabled.invoke(wifiManager, wifiConfig,
                    enabled);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
  • 扫描WIFI热点

    开始扫描热点

wifiManager.startScan()

扫描结果会在BroadcastReceiver且意图action为WifiManager.SCAN_RESULTS_AVAILABLE_ACTION时被接收

  • 连接到指定的WIFI热点
    遍历扫描到的热点列表,当发现满足要求的热点时,创建连接到该热点的配置信息(密码、加密类型要正确)
    /**
     * 创建链接网络的wifi配置信息,详见详见系统Setting
     * App的(低版本看WifiDialog.java,高版本看WifiConfigController.java)
     * 
     * @param ssid
     *            热点名称
     * @param password
     *            密码可以为8-63个ASCII码或8-64个十六进制的字符
     * @return
     */
    public WifiConfiguration createWifiConnectConfiguration(int wifiApType,
            String ssid, String password) {
        if (TextUtils.isEmpty(ssid)) {
            throw new RuntimeException("ssid should not be null");
        }
        WifiConfiguration config = new WifiConfiguration();
        config.SSID = WifiUtils.convertToQuotedString(ssid);
        switch (wifiApType) {
        case WifiApType.TYPE_NONE:
            config.allowedKeyManagement.set(KeyMgmt.NONE);
            break;
        case WifiApType.TYPE_WPA_PSK:
        case WifiApType.TYPE_WPA2_PSK:
            if (TextUtils.isEmpty(password) || password.length() < 8) {
                throw new RuntimeException("密码至少为8个字符");
            }
            config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
            if (password.matches("[0-9A-Fa-f]{64}")) {
                config.preSharedKey = password;
            } else {
                config.preSharedKey = WifiUtils.convertToQuotedString(password);
            }
            break;
        }
        return config;
    }

连接到指定热点

    /**
     * 连接到指定热点
     * 
     * @param accessPoint
     *            热点信息
     * @param password
     *            密码,热点安全类型为WPA_PSK或者WPA2_PSK,密码必填
     * @return
     */
    public boolean connectToAccessPoint(AccessPoint accessPoint, String password) {
        if (accessPoint == null) {
            return false;
        }
        int apType = WifiApManager.WifiApType.TYPE_NONE;
        switch (accessPoint.getSecurity()) {
        case AccessPoint.SECURITY_NONE:
            break;
        case AccessPoint.SECURITY_WPA_PSK:
            apType = WifiApManager.WifiApType.TYPE_WPA_PSK;
            if (TextUtils.isEmpty(password)) {
                throw new IllegalArgumentException(
                        "when accessPoint security is WPA_PSK,the password should not be null");
            }
            break;
        case AccessPoint.SECURITY_WPA2_PSK:
            apType = WifiApManager.WifiApType.TYPE_WPA2_PSK;
            if (TextUtils.isEmpty(password)) {
                throw new IllegalArgumentException(
                        "when accessPoint security is WPA2_PSK,the password should not be null");
            }
            break;
        }
        // 如果已经保存的wifi,里面有networkId,可以直接连接
        int networkId = accessPoint.getNetworkId();
        if (networkId == -1) {
            WifiConfiguration config = wifiApManager
                    .createWifiConnectConfiguration(apType,
                            accessPoint.getSsid(), password);
            networkId = wifiApManager.wifiManager.addNetwork(config);
        }
        return wifiManager.enableNetwork(networkId, true);

    }

四、总结

通过第三部分的实现,WIFI通信模块的功能差不多已经实现完毕,上面所用到代码都在WifiLibrary[https://github.com/Money888/WifiLibrary]中,欢迎大家下载阅读。
在后面的两篇实现文章中,会介绍UDP和TCP的通信实现,敬请期待。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值