• Android11 Wifi 加密类型详解


    Android Wifi 加密类型详解

    有些项目中是不用原生Settings,是自己开发的设置,连接WPA/WPA2是没有问题的,
    但是连接EAP和WPA3居然连接不上,发现自己的代码中并没有对这两种加密类型的wifi就行判断,导致连接不上。

    如果你是类似的问题或者自己开发的wifi连接,并且设计到多种加密形式,可以看看本文内容。

    一、早期的WiFi认证标准

    WEP:  Wired Equivalent Privacy(有线等效加密) ,
    保护无线网络信息安全的体制,无线网络通过无线电传播,为了保证数据机密性而设计。
    但是存在一些漏洞,比如针对EAP的密钥截取攻击,和针对数据完整性的Replay Attack(回放攻击),已经不推荐使用。
    
    WPA:  WiFi Protected Access   (Wi-Fi访问保护),
    是一种保护无线网络访问安全的技术标准,WPA是为了解决EAP的几个严重的弱点而产生的,
    通过128位密钥加上48位初向量和可以在使用中动态改变密钥的“临时密钥完整性协议”(TKIP),
    使用RC4加密、使用Michael消息验证码,避免针对EAP的密钥截取攻击和Replay Attack。
    
    WPA2:WPA的升级版,
    采用CCMP计数器模式密码块链消息完整码协议,使用CBC-MAC替代Michael消息验证码,
    使用AES取代RC4加密算法。在身份认证和保证数据完整性上更安全。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Android 10之后有了WPA3 后面有介绍。

    二、Android 中Wifi加密类型

    1、代码中的声明

    WifiConfiguration.java (Android 11)

        /** Security type for an open network. */
        public static final int SECURITY_TYPE_OPEN = 0;
        /** Security type for a WEP network. */
        public static final int SECURITY_TYPE_WEP = 1;
        /** Security type for a PSK network. */
        public static final int SECURITY_TYPE_PSK = 2;
        /** Security type for an EAP network. */
        public static final int SECURITY_TYPE_EAP = 3;
        /** Security type for an SAE network. */
        public static final int SECURITY_TYPE_SAE = 4;
        /** Security type for an EAP Suite B network. */
        public static final int SECURITY_TYPE_EAP_SUITE_B = 5;
        /** Security type for an OWE network. */
        public static final int SECURITY_TYPE_OWE = 6;
        /** Security type for a WAPI PSK network. */
        public static final int SECURITY_TYPE_WAPI_PSK = 7;
        /** Security type for a WAPI Certificate network. */
        public static final int SECURITY_TYPE_WAPI_CERT = 8;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在文件 WifiConfiguration.java搜索"Security types we support.",也能看到当前api支持的类型。

    0表示不加密。Android11 存在的wifi加密方式8种。
    对比查看了Android9.0只有1-6,后面新增的两种也没用过。
    Android11 WiFi相关代码改动记录 https://blog.csdn.net/qq_43804080/article/details/112602931

    正常手机和路由器发出热点都是0/1/2;

    2、Wifi加密协议简介

    下面是wifi加密介绍,东拼西凑搞过来的:

    
    SECURITY_TYPE_WEP = 1; //比较旧的加密方式,基本很少用,不太安全
        更多文字介绍:https://blog.csdn.net/u013403237/article/details/50663790
    
    SECURITY_TYPE_PSK = 2; //目前常用
        包含WPA/WPA2
        更多文字介绍:https://www.jianshu.com/p/9316c433ec5f/
    
    SECURITY_TYPE_EAP = 3; //非常安全
        EAP 的类型,是一种企业验证的安全类型,EAP 全称叫 802.1X/EAP 他常常给误解成 802.11x。
        EAP 的意思就是可扩展认证协议的缩写。
        最常部署的 EAP 验证类型包括 EAP-MD-5、EAP-TLS、EAP-LEAP、EAP-TTLS、EAP-Fast、EAP-PEAP;
        所以EAP网络也是连接wifi参数最多的网络,还需要添加客户端和服务器端的证书安装。
        更多文字介绍:https://blog.csdn.net/hl1293348082/article/details/123888636
    
    
    SECURITY_TYPE_SAE = 4;
    
        SAE最早是802.11s中为mesh网络提出的基于password的认证和key生成协议[1]。在WPA2-PSK中,PMK就是PSK,直接来源于密钥;
        而SAE则保证任何STA pair(AP-STA,nonAP-STA)在不同的session都有不同的PMK,使用SAE认证的个人无线网络通常称为WPA3-PSK/WPA3-Personal。
        简单的说SAE,就是WPA2的升级,简称WPA3
        更多文字介绍:https://blog.csdn.net/qq_23087099/article/details/113921261
    
    SECURITY_TYPE_EAP_SUITE_B = 5;
        EAP网络的另一种加密形式,具体介绍查不出
    
    
    SECURITY_TYPE_OWE = 6;
        未查出相关介绍,在当前类中发现,入下介绍,Opportunististic (共产主义),大概是共享网络的一种加密格式吧。
            /**
             * Opportunististic Wireless Encryption
             */
            public static final int OWE = 9;
    
    
    最后两种:SECURITY_TYPE_WAPI_X 
    
    WAPI(无线网络WLANAuthenticationandPrivacyInfrastructure)是我国自主研发并大力推行的无线网络WLAN安全标准,
    它通过了IEEE(注意,不是Wi-Fi)认证和授权,是一种认证和私密性保护协议,其作用类似于802.11b中的WEP,但是能提供更加完善的安全保护。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3、代码中配置加密类型

    连接对应类型的wifi需要配置里面的config对象,
    可以使用 WifiConfiguration 的 setSecurityParams 进行设置。

    WifiConfiguration.java

    
    
        /**
         * Set the various security params to correspond to the provided security type.
         * This is accomplished by setting the various BitSets exposed in WifiConfiguration.
         *
         * @param securityType One of the following security types:
         * {@link #SECURITY_TYPE_OPEN},
         * {@link #SECURITY_TYPE_WEP},
         * {@link #SECURITY_TYPE_PSK},
         * {@link #SECURITY_TYPE_EAP},
         * {@link #SECURITY_TYPE_SAE},
         * {@link #SECURITY_TYPE_EAP_SUITE_B},
         * {@link #SECURITY_TYPE_OWE},
         * {@link #SECURITY_TYPE_WAPI_PSK}, or
         * {@link #SECURITY_TYPE_WAPI_CERT}
         */
    
        public void setSecurityParams(@SecurityType int securityType) {
            // Clear all the bitsets.
            allowedKeyManagement.clear();
            allowedProtocols.clear();
            allowedAuthAlgorithms.clear();
            allowedPairwiseCiphers.clear();
            allowedGroupCiphers.clear();
            allowedGroupManagementCiphers.clear();
            allowedSuiteBCiphers.clear();
    
            switch (securityType) {
                case SECURITY_TYPE_OPEN:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                    break;
                case SECURITY_TYPE_WEP:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                    allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
                    allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
                    break;
                case SECURITY_TYPE_PSK:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
                    break;
                case SECURITY_TYPE_EAP:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
                    break;
                case SECURITY_TYPE_SAE:
                    allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                    requirePmf = true;
                    break;
                case SECURITY_TYPE_EAP_SUITE_B:
                    allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                    allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
                    // Note: allowedSuiteBCiphers bitset will be set by the service once the
                    // certificates are attached to this profile
                    requirePmf = true;
                    break;
                case SECURITY_TYPE_OWE:
                    allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                    requirePmf = true;
                    break;
                case SECURITY_TYPE_WAPI_PSK:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
                    allowedProtocols.set(WifiConfiguration.Protocol.WAPI);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4);
                    break;
    
                case SECURITY_TYPE_WAPI_CERT:
                    allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_CERT);
                    allowedProtocols.set(WifiConfiguration.Protocol.WAPI);
                    allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4);
                    allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4);
                    break;
                default:
                    throw new IllegalArgumentException("unknown security type " + securityType);
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    4、系统源码应用中设置加密类型

    在TvSetting和原生Setting里面会调用自己SettingsLib中的AccessPoint的相关api,而里面的加密类型的数值又有点不一样。

    AccessPoint 是对WifiConfiguration 的进一步封装对象。

    frameworks\base\packages\SettingsLib\src\com\android\settingslib\wifi\AccessPoint.java

    public static final int SECURITY_NONE = 0;
    public static final int SECURITY_WEP = 1;
    public static final int SECURITY_PSK = 2;
    public static final int SECURITY_EAP = 3;
    public static final int SECURITY_OWE = 4;
    public static final int SECURITY_SAE = 5;
    public static final int SECURITY_EAP_SUITE_B = 6;
    public static final int SECURITY_MAX_VAL = 7;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    普通应用中使用api,扫描到的wifi 列表对象是WifiConfiguration对象;
    所以你如果使用SettingsLib的api方式,扫描到的AccessPoint列表,
    就需要逐个判断类型设置 WifiConfiguration 的setSecurityParams方法来设置加密类型的配置。

    看了下原生Settings里面的代码,是自己判断加密类型,自己写一遍setSecurityParams方法。

    (1)原生Settings应用

    比如,Settings的WifiConfigController.java的代码

      public WifiConfiguration getConfig() {
            if (mMode == WifiConfigUiBase.MODE_VIEW) {
                return null;
            }
    
            WifiConfiguration config = new WifiConfiguration();
    
            switch (mAccessPointSecurity) {
                case AccessPoint.SECURITY_NONE:
                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
                    break;
    
                case AccessPoint.SECURITY_WEP:
                    // 加密类型配置
                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
                    // 密码设置
                    if (mPasswordView.length() != 0) {
                        int length = mPasswordView.length();
                        String password = mPasswordView.getText().toString();
                        // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                        if ((length == 10 || length == 26 || length == 58)
                                && password.matches("[0-9A-Fa-f]*")) {
                            config.wepKeys[0] = password;
                        } else {
                            config.wepKeys[0] = '"' + password + '"';
                        }
                    }
                    break;
        。。。。。。
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    大屏开发人员经常参考的是TvSetting的修改。

    (2)原生TvSettings应用

    也可以对config里面的allowedProtocols直接设置,
    比如:TvSettings 的WifiSecurityHelper.java

     public static WifiConfiguration getConfig(FragmentActivity context) {
    
            WifiConfiguration config = userChoiceInfo.getWifiConfiguration();
    
            if (TextUtils.isEmpty(config.SSID)) {
                // If the user adds a network manually, assume that it is hidden.
                config.hiddenSSID = true;
            }
    
            if (userChoiceInfo.getPageSummary(UserChoiceInfo.SSID) != null) {
                config.SSID = AccessPoint.convertToQuotedString(
                        userChoiceInfo.getPageSummary(UserChoiceInfo.SSID));
            }
            int accessPointSecurity = getSecurity(context);
            String password = userChoiceInfo.getPageSummary(UserChoiceInfo.PASSWORD);
            int length = password != null ? password.length() : 0;
    
            switch (accessPointSecurity) {
                case AccessPoint.SECURITY_NONE:
                    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                    break;
                case AccessPoint.SECURITY_WEP:
                    // 加密类型配置
                    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                    config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
                    config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
                    //设置密码
                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                    if ((length == 10 || length == 26 || length == 58)
                            && password.matches("[0-9A-Fa-f]*")) {
                        config.wepKeys[0] = password;
                    } else {
                        config.wepKeys[0] = '"' + password + '"';
                    }
                    break;
                case AccessPoint.SECURITY_PSK:
                    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
                    if (length != 0) {
                        if (password.matches("[0-9A-Fa-f]{64}")) {
                            config.preSharedKey = password;
                        } else {
                            config.preSharedKey = '"' + password + '"';
                        }
                    }
                    break;
                case AccessPoint.SECURITY_SAE:
                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
                    if (length != 0) {// DroidLogic modify SWPL-45229
                        config.preSharedKey = '"' + password + '"';
                    }
                    break;
                case AccessPoint.SECURITY_OWE:
                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
                    break;
    
                default: //TVSettings只支持上面六种加密方式的wifi,其他的wifi就要参考原生Settings修改了
                    return null;
            }
            config.setIpConfiguration(advancedOptionsFlowInfo.getIpConfiguration()); //配置代理/静态ip
            return config;
            }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    所以看到下面不同的代码方式对加密类型的配置不要大惊效果

    
    WifiConfiguration config = new WifiConfiguration();
    
    //方式1 配置加密类型
        config.setSecurityParams(XX);
    
    //方式2 配置界面类型
    本质也是设置下面的加密类型配置(,下面只是示例),不同的加密类型配置的项不同,具体参考WifiConfiguration.java即可。
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    简单的看,使用 WifiConfiguration 的 setSecurityParams 方法是比较简单和保险的,但是确认下类型对应哦。

    值得注意的是不同的加密类型,有的设置密码会有一定的差异。
    WEP某些情况,PSK(WAP1/2)、SAE(WPA3)的密码如果有特殊符合是要加双引号""包裹的。
    具体的代码可以看Settings的WifiConfigController.java的具体获取config对象的代码,不同的Android版本里面支持的加密协议有一定的变化,一般来说是会变多。

    5、获取加密类型

    WifiConfiguration 对象居然没有get方法获取到它的加密类型!

    系统封装的 AccessPoint 对象是有的方法getSecurity 和getSecurityString 获取加密类型和类型字符串;

    那么如何从WifiConfiguration对象中获取加密类型呢?

    查看里面的代码发现有个隐藏的方法:

    
      /** @hide
         *  return the SSID + security type in String format.
         */
        public String getSsidAndSecurityTypeString() {
            String key;
            if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
            } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
                    || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
            } else if (wepTxKeyIndex >= 0 && wepTxKeyIndex < wepKeys.length
                    && wepKeys[wepTxKeyIndex] != null) {
                key = SSID + "WEP";
            } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
            } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
            } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
            } else if (allowedKeyManagement.get(KeyMgmt.WAPI_PSK)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_PSK];
            } else if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_CERT];
            } else if (allowedKeyManagement.get(KeyMgmt.OSEN)) {
                key = SSID + KeyMgmt.strings[KeyMgmt.OSEN];
            } else {
                key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
            }
            return key;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    隐藏的方法没法用啊!

    但是发现 allowedKeyManagement 对象是public 的,是可以在外部普通使用的,所以你懂的!

    
         /**
         * The set of key management protocols supported by this configuration.
         * See {@link KeyMgmt} for descriptions of the values.
         * Defaults to WPA-PSK WPA-EAP.
         */
        @NonNull
        public BitSet allowedKeyManagement;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    你可以自己封装getSecurity 和getSecurityString方法使用。

    三、wifi连接

    Wifi 连接的主要代码:

        protected void connect(WifiConfiguration config) {
            WifiManager wifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
            wifiManager.connect(config, connectListener); //所有的数据都在config对象里面;connectListener 没啥作用,一般传null参数,不管密码是否正确都是回调success。
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中WifiConfiguration就是我们要配置的数据,其中包括加密类型数据,密码,代理和静态ip。

    系统应用的连接可以参考我之前写的这个:
    https://blog.csdn.net/wenzhi20102321/article/details/123675077

    本文的wifi加密协议就介绍到这里,如果自己的wiif不能连接某个加密的网络,还是要用原始的Settings连接试试,确认是系统环境原因还是自己的apk代码问题。

    共勉: 逆水行舟不进则退。其实很多时候,某一方面的知识就那么多,你多学一点,就多会一点。

  • 相关阅读:
    String10:StringBuffer类结构剖析
    【爬虫实战】利用代理爬取电商数据
    编译和链接
    在使用tomcat运行项目时,遇到端口80被占用的情况问题解决
    【Linux】自旋锁 以及 读者写者问题
    彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-模板与数据库EP02
    (Python)MATLAB mat矩阵和Python npy矩阵转换
    Unity的IPreprocessBuildWithReport:深入解析与实用案例
    【23种设计模式】单一职责原则
    2023面试知识点一
  • 原文地址:https://blog.csdn.net/wenzhi20102321/article/details/126219495