1 wpa_supplicant 源码下载地址:

wpa_supplicant-2.10 下载地址:wpa_supplicant-2.10

2 wpa_supplicant 学习总结

wpa_supplicant 学习参考网址:wpa_supplicant

2.1 wpa_supplicant 基本概念:

  • ** wpa_supplicant 是什么?**
    wpa_supplicant 是WPA 请求者,支持WPA 和WPA2(IEEE802.11i/RSN).
  • ** wpa_supplicant 能干什么?**
    wpa_supplicant 被设计成一个“守护程序”,在后台运行充当控制无线连接的后端,主要实现与WPA 身份验证器的秘钥协商,并控制wlan驱动程序的漫游和IEEE802.11身份验证/关联。
  • ** wpa_supplicant 支持哪些功能?**
    WPA-PSK (“WPA-Personal”)
    带有 EAP 的 WPA(例如,使用 RADIUS 身份验证服务器)(“WPA-Enterprise”)
    CCMP、TKIP、WEP104、WEP40 的密钥管理
    WPA 和完整的 IEEE 802.11i/RSN/WPA2
    RSN:PMKSA 缓存、预身份验证
    IEEE 802.11r
    IEEE 802.11w
    Wi-Fi 保护设置 (WPS)

2.2 wpa_supplicant 目录介绍:

  • hostapd:
    提供AP 功能
  • wpa_supplicant:
    提供Station & P2P 模式
  • src:
    存放hostapd & wpa_supplicant 通用的数据结构和处理方法。 hostapd/src & wpa_supplicant/src 链接到此src 目录。

2.3 wpa_supplicant 架构:

  • wpa_supplicant 是C/S 结构中的Server 端,通过ctrl i/f 向客户提供通信接口。客户可以使用wpa_cli 将cmd 下发wpa_supplicant.
  • wpa_supplicant 所有工作都围绕事件循环(event loop).事件循环基于事件驱动,主线程等待事件发货所能并处理他们。
  • wpa_supplicant 需要很多配置,configuration 模块负责该配置。
  • EAP & EAPOL 是LLC 层数据,I2_packet 模块负责收发EAP & EAPOL 消息。
  • driver i/f 属于driver wrapper,实现驱动无关性。driver_wrapper 与芯片和底层驱动有关。

2.4 wpa_supplicant 源码编译及产物

2.4.1 源码编译方法:

----后续追加----

2.4.2 编译产物:

wpa_supplicant 编译完会生成wpa_supplicant ,同时也会生成wpa_cli.

2.5 wpa_supplican 中wpa_cli 使用:

2.5.1 wpa_cli 基本用法及理解:

$: ifconfig 查看当前网络节点,如有wlan0
$ wpa_cli -i wlan0
$> help   //wpa_cli 的所有指令帮助
$> q      //退出wpa_cli 命令行

wpa_supplicant 会按照以下方式回应相关信息:

  • CTRL-REQ<><><>
  • CTRL-RSP<><><>
  • CTRL-EVENT-XXX 事件通知
  • WPA:XXX 通知事件
  • WPS-XXXX 通知事件

2.5.2 wpa_cli 启动p2pl 连接:

启动wpa_supplicant:
1. 启动wpa_supplicant
/usr/local/bin/wpa_supplicant -iwlan0 -Dnl80211 -c /usr/local/etc/wifimanager/wpa_0_8.conf -dddd&2. ctrl interface 切换到p2p-dev-wlan0
wpa_cli -ip2p-dev-wlan03. 命令行执行p2p_find
p2p_find 604. 扫描到p2p device 后,停止find:
p2p_stop_find5. 根据find 到设备的mac 地址进行连接
p2p_connect xxx pbc go_intent=15

2.5.3 wpa_cli 启动p2pg ,手机搜索并加入p2pg:

1. 启动wpa_supplicant
/usr/local/bin/wpa_supplicant -iwlan0 -Dnl80211 -c /usr/local/etc/wifimanager/wpa_0_8.conf -dddd&2. ctrl interface 切换到p2p-dev-wlan0
wpa_cli -ip2p-dev-wlan03. 启动p2pg:
p2p_group_add freq=54. 手机搜索并加入p2pG5. 收到P2P-PROV-DISC-PBC-REQ, 执行wps_pbc
//wpa_cli -i p2p-wlan0-0  有些网卡需要切换ctrl_interface 到p2p-wlan0-0.
wps_pbc

2.6 wpa_supplicant 配置文件说明:

设备不同存放配置文件会有所差异,umaster code 配置文件目录如下:/usr/local/etc/wifimanager/wpa_0_8.conf
常用配置信息说明如下:

  • ctrl_interface
    //配置所用的组件,这里为wpa_supplicant

  • p2p_no_group_iface=1
    //realtek 要求添加的配置项,否则只有root权限才可以读取wpa配置。

  • update_config=1
    //是否允许修改配置后wpa_supplicant可以覆盖配置文件,update_config=1表示wpa_cli 和wpa_gui能够永久修改配置。

  • device_name=Default_p2p
    //设备名称,UTF8编码的32个字节

  • manufacturer=Realtek
    //设备的制造商(64个ASCII特征码)

  • model_number=WLAN_CU
    //设备型号(32个ASCII特征码)

  • serial_number=1234
    //设备的序列号(32个ASCII特征码)

  • device_type=1-0050F204-1
    //设备的主要类型:
    • # 1-0050F204-1 (Computer / PC)
    • # 1-0050F204-2 (Computer / Server)
    • # 5-0050F204-1 (Storage / NAS)
    • # 6-0050F204-1 (Network Infrastructure / AP)

  • os_version=01020300
    //操作系统版本

  • config_methods=virtual_dusplay virtual_push_keypad
    //配置方法,虚拟显示、虚拟按钮、键盘

  • wfd_tcpport=
    // WFD 所用的control 端口,默认是7326[30]

  • wfd_device_type=
    WFD 所选的device type;

    WFD 参考网址:https://blog.csdn.net/sui1005316018/article/details/102745952

2.7 wpa_supplicant 结构及相关API

2.7.1 wpa_ctrl 对象及相关接口说明:

接口/对象 说明
wpa_ctrl.h/wpa_ctrl 对象 客户端使用的控制对象
struct wpa_ctrl* wpa_ctrl_open(const char *ctrl_path) 获取wpa_ctrl 对象
void wpa_ctrl_close(struct wpa_ctrl *ctrl); 销毁wpa_ctrl 对象
int wpa_ctrl_request(struct wpa_ctrl ctrl, const char cmd, size_t cmd_len,char *replay,szie_t *replay_len, void(*msg_cb)(char *msg, size_t len)); 客户端给wpa_supplicant 发送消息。 发送内容保存至cmd中,wpa_supplicant 执行结果放到replay 中。 如果在执行此cmd 过程中,wpa_supplicant 有消息过来则可以通过msg_cb 来获取处理。 但前提是ms_cd 有提前打开事件通知监听. 这是堵塞的动作,一般至少等待2s钟用来接受反馈的回复消息。
int wpa_ctrl_attathc(struct wpa_ctrl *ctrl) 打开通知事件监听功能
int wpa_ctrl_detach(struct wpa_ctrl *ctrl) 关闭通知事件监听功能
int wpa_ctrl_recv(struct wpa_ctrl * ctrl, char *replay, size_t *replay_len ) 打开通知事件监听功能的wpa_ctrl 才可以使用该接口获取wpa_supplicant 的消息,该接口仅仅是获取信息。// 这个接口是一个堵塞的操作,当没有可用的消息时,就会一直堵塞。
int wpa_ctrl_pending(struct wpa_ctrl *ctrl) 用来检测是否有即将到来的事件消息
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) 获取控制接口的文件描述符
wpa_ctrl_cleanup() 关闭之前创建的wpa_ctrl 对象,清除所有socket file.
char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) 获取wpa_ctrl 对应的ifname.

通常用法:

  • 客户端创建两个wpa_ctrl对象,一个打开事件监听功能的wpa_ctrl 对象将只通过wpa_ctrl_recv 来接收通知事件。
  • 另一个wpa_ctrl 专职发送命令和接收回复,由于没有wpa_ctrl_attach ,因此不会受到事件通知。

2.7.2 wpa_interface 结构说明:

  • wpa_interface 用来描述一个无线网络设备。wpa_supplicant 可以支持操作多个无线网络设备,需要添加到wpa_supplicant 中。

  • wpa_interface 定义在wpa_supplicant/wpa_supplicant_i.h 中

结构成员 成员作用
const char * confname 接口对应的配置文件的名称,wpa_supplicant.conf
const char * confanother 是接口对应扩展配置文件的名称,如果没有使用设置为NULL
const char * ctrl_interface 控制接口文件即控制接口unix域socket 地址。//如果配置文件没有设置,则使用该项设置interface 的相关参数。如果有配置文件,则此项会被配置文件中的值覆盖。
const char * driver 底层驱动选择,如wext、nl80211等;如果设置NULL 则使用默认driver
const char *driver_param 驱动参数 ;//如果配置文件没有设置,则使用该参数作为驱动参数;如果配置文件有设置,则此项会被配置文件覆盖。
const char * ifname 网络接口设备名,如wlan0
const char * bridge_ifname 桥接时使用,如br0,较少使用。 // 可选的桥式接口名称,如果允许额外的interface 接收EAPOL 可以设置该项,较少使用。
int p2p_mgmt p2p管理。 //指示wpas_p2p_init() 是否必须呼叫该接口,主要用于当驱动支持专用P2P Device interface 而不是network interface 时。

2.7.3 wpa_params 结构说明:

  • 该结构用于wpa_supplicant_init 中,主要是运行的参数。
  • 定义在wpa_supplicant/wpa_supplicant_i.h 中
结构成员 成员作用
int daemonize 是否在后台运行wpa_supplicant 程序
int wait_for_monitor 在wpa_supplicant starting 前是否等待monitor program
char *pid_file 指定PID File 的路径。 //如果daemonize 和该值设置,则后台进程也将会写入特定文件。
int wpa_debug_level 设置debug level, 比如MSG_INFO
int wpa_debug_show_keys 在debug信息中是否包含关键信息。开发阶段和debug 阶段可以打开,正常关闭
int wpa_debug_timestamp 在debug 信息中是否包含时间信息
char *ctrl_interface 全局ctrl_iface 路径/参数
char *ctrl_interface group 全局ctrl_iface group
int dbus_ctrl_interface 使能DBus control interface
const char *wpa_debug_file_path 指定debug file, 如果设置NULL则使用标准输出
int wpa_debug_syslog 通过syslog使能log 输出
int wpa_debug_tracing 通过linux tracing 使能log 输出
char *override_driver 在动态interface 使用该参数值覆盖driver parameter,强迫使用特定的driver wrapper.
char *override_ctrl_interface; 在动态interface 中,使用该参数覆盖ctrl_interface 参数
char *entropy_file 配置wpa_supplicant自己的entropy 存储当重新启动时
char *conf_p2p_dev 通常设置为NUll

2.7.4 wpa_supplicant 结构说明:

  • 一个interface 对应一个wpa_supplicant
  • wpa_supplicant 定义在wpa_supplicant_i.h 中
结构成员 成员作用
struct wpa_global *global 指向wpa_global 对象
struct wpa_supplicant *next 进程内所有wpa_supplicant 对象都保存在一个单项链表中
struct l2_packet_data *l2 处理EAP和EAPOL 信息
unsigned char own_addr[ETH_ALEN] 地址
char ifname[100] ifname 如wlan0
char *confname 配置文件,wpa_supplicant.conf
struct wpa_conf *conf !! 从配置文件中解析出的配置信息
int countermeasures
u8 bssid[ETH_ALEN] 表示当前supplicant 所连接诶的无线网络BSSID
u8 pending_bssid[ETH_ALEN] 表示当前supplicant 还处于关联状态时,目标BSSID
int disconnected 表示此supplicant 是否被禁止链接到无线网络
struct wpa_ssid *current_ssid 当前使用的wpa_ssid 对象
struct wpa_bss *current_bss 当前使用的wpa_bss对象。 //wpa_bss 对象是无线网络在wpa_supplicant 的代表,包括无线网络的频率, 心跳间隔,capability 信息等
int pairwise_cipher 当前supplicant单播数据加密类型
void *drv_priv 驱动对应上下文信息。driver i/f 接口定义了两个初始化函数,比nl80211 为例有global_init 和init2. init2 返回driver wrapper 上下文信息保存在此结构中
void *global_drv_priv 驱动对应的全局上下文信息。 而global_init 返回值为driver wrapper 全局上下文信息,则保存到wpa_global 的drv_priv 中。
int sched_scan_timeout 计划扫描timeout时间 。该变量与定时扫描功能有关,要求kernel version > 3.0 的wifi 驱动支持。 启动定时扫描需要设置扫描间隔
const struct wpa_driver_ops *driver 此supplicant 对应的驱动对象
struct wpa_sm *wpa wpa 状态机
struct eapol_sm *eapol eapol 状态机
struct ctrl_iface_priv *ctrl_iface 此supplicant 对应的控制接口对象
enum wpa_states wpa_state !!!! supplicant 当前的状态 (详见解释)
struct wpa_blacklist *blacklist //黑名单,wpa_supplicant 不再连接黑名单中的网络
const struct bgscan_ops *bgscan background 扫描功能 。与后台扫描及漫游技术有关,为增加AP无缝切换,可以bgscan 当发现AP信号强度低于某个阈值时就切换到信号更强的AP。

wpa_states 取值:

  • WPA_DISCONNECTED: 当前未连接任何无线网络
  • WPA_INTERFACE_DISABLED:当前wpa_supplicant 所使用的网络设备被禁用
  • WPA_INACTIVE:表示当前wpa_supplicant 没有可连接的无线网络,比如周围无无线网络,或周围有无线网络但没有配置信息(如无设置密码)不能发起认证和关联请求。
  • WPA_SCANNING: 当前wpa_supplicant 正在扫描无线网络中
  • WPA_AUTHENTICATING: 当前wpa_supplicant 正在身份验证中
  • WPA_ASSOCIATING: 当前wpa_supplicant 正在关联过程中
  • WPA_ASSOCIATED: 表明此wpa_supplicant 成功关联到某个AP
  • WPA_4WAY_HANDSHAKE: 表明此wpa_supplicant 处于4次握手处理过程中。PSK (WPA/WPA2-personal)策略 STA 收到第一个EAPOL 数据包就进入该状态,而WPA-Enterprise 完成身份验证就进入此状态。
  • WPA_GROUP_HANDSHAKE:表明STA处于组秘钥握手协议处理过程中。STA完成4次握手并收到组播秘钥交换第一帧数据后进入此状态。
  • WPA_COMPLETED: 所有认证过程完成,wpa_supplicant 正式加入某个无线网络。

2.7.5 wpa_global 结构说明:

  • wpa_global 是一个全局性质的上下文信息。
  • wpa_global 对象通过wpa_supplicant_init()获得
  • 定义在wpa_supplicant_i.h 中
结构成员 成员作用
struct wpa_supplicant* ifaces ifaces 指向wpa_supplicant 对象链表,一个interface 对应一个wpa_supplicant 对象。
struct wpa_params params 运行参数
struct ctrl_iface_global_priv* ctrl_ifaces 全局控制接口的信息,包含一个用于通信的socket 句柄
void **drv_priv drv_priv 包含driver wrapper 所需的全局上下文信息
size_t drv_count driver wrapper 中的个数。
  • wpa_global 有一个全局控制接口,如果设置该接口,其它wpa_interface 设置的控制接口将被替代。

2.7.6 wpa_ssid 结构和说明:

  • wpa_ssid 用于存储某个无线网络的配置信息(如支持的安全类型,优先级等)。
  • 该结构是wpa_supplicant.conf 中无线网络配置项在代码中的反应
结构成员 成员作用
struct wpa_ssid *next 在global list 中下一个network. //该表的HEAD 存储在wpa_config 中ssid
struct wpa_ssid *pnext 在per-priority list 中的Next network. // 该表的HEAD 存储在wpa_config 的pssid
int id network 的唯一标识符。 当network 创建时会分配唯一的id.
int priority priority group. //默认该值都为0,但是有些网络有详细优先级信息,优先级高的网络会更快扫描和连接。
u8 *ssid 网络热点名称
size_t ssid_len ssid 的长度
u8 bssid[ETH_ALEN] 连接AP 时使用,如果是p2p group ,该参数代表GO 的Device Address.
u8 *bssid_blacklist BSSID 的黑名单,不允许连接的网络
u8 *bssid_whitelist BSSID 白名单,允许访问的BSSID
int bssid_set 当前网路是否有配置BSSID
u8 go_p2p_dev_addr[ETH_ALEN] GO 的P2P Device 地址
u8 psk[32] psk 共享秘钥
int psk_set psk 是否配置 ,如果该项配置则psk 使用SSID 和passphrase 参数
char *passphrase 8-63 个字符, 该变量只和WPA/WPA2-PSK 模式有关,用于存储输入的字符串密码,但规范中要求的也是PSK.,所以WPA_Supplicant 根据会它和ssid 进行一定计算得到最终使用的PSK.
char *sae_password SAE Password
int pairwise_cpher 加密套件,该参数是用于单播数据加密的秘钥类型(即数据收发两方使用的数据加密方法),如CCMP/TKIP/WEP 等
int group_cpher 用于组播加密的秘钥类型
int key_mgnt 秘钥管理类型,默然是PSK | IEEE802.X
int proto 该无线网络支持的安全保护类型,有WPA 和RSN. 默认是WPA|RSN
int auth_alg 该无线网络支持的身份验证类型,常用的是包括Open System (如果使用WPA 或RSN 必须选择Open System)和Shared key .定义在defs.h 中
int scan_ssid 是否使用probe request 扫描此ssid 对应的无线网络
int eapol_flags: 与WEP Key 有关
struct epa_peer_config eap eap peer 端的设置信息
int proactive_key_caching 秘钥暂存功能,与OPC(Opportunistic PMK Caching )技术有关。(1个控制器+多个ap 组成的架构)Sta与同一个ZONE 中的ap0 身份验证得到PMK0 后,当切换到其它AP时不需要再进行身份验证,其它ap1 根据pmk0 得到pmk1.直接进行后面的握手环节
enum wpas_mode mode 网络模式中的组件类型
int disabled 0: 无线网络可用;1:禁止使用;2:该无线网络和p2p 有关

pairwise_cipher 常用加密套件:

wpa_supplicant/src/common/defs.h
#define WPA_CIPHER_NONE BIT(0)
#define WPA_CIPHER_WEP40 BIT(0)
#define WPA_CIPHER_WEP104 BIT(0)
#define WPA_CIPHER_TKIP BIT(0)   //加密套件TKIP
#define WPA_CIPHER_CCMP BIT(0)  //加密套件CCMP

key_mgmt:
该成员和802.11 中的AKM suit 相关。AKM(Authentication and key management 身份验证和秘钥管理) suite 定义一套算法用于Supplicant 和Authenticator 之间交换身份和秘钥信息。
定义在wpa_supplicant/src/common/defs.h

默认的AKM suite 是PSK+802.X, 在wpa_supplicant/config_ssid.h

wpas_mode:

  • WPAS_MODE_INFRA=0: 代表基础网络结构的STA
  • WPAS_MODE_IBSS=1;代表IBSS 网络的模式
  • WPAS_MODE_AP=2;代表基础结构型网络中的AP
  • WPAS_MODE_P2P_GO=3; 代表GO。

2.7.7 eloop 事件循环 相关 结构和API

结构/API 含义
struct eloop_data()
eloop_register_read_sock() 注册读事件, 当socket 上有读事件就会触发handle 函数
eloop_register_sock() 注册读,写,异常,timeout 事件,根据传入参数决定
eloop_register_signal() 注册信号事件
eloop_sock_table_set_fds() 将外界设置的读、写、异常等事件添加到fd_set 中去
eloop_sock_table_dispatch(&eloop.reads, rfds) 处理读事件,写,异常事件类似。

wpa_supplicant.c wpa_supplicant_init_iface() 根据配置文件初始化interface

2.7.7 driver 相关结构及API

supplicant/src/driver/driver.c 中
const struct wpa_driver_ops *const wpa_drivers[]={&wpa_driver_nl80211_ops,    // 获取热点等对应接口......
}

wifi spec 下载网址:

WPA3-Specification 下载:WPA3_SPEC
https://www.wi-fi.org/download.php?file=/sites/default/files/private/WPA3_Specification_v3.0.pdf

wiFI基础知识----wpa_supplicant相关推荐

  1. Android 系统 wifi基础知识

    第3章 Wi-Fi基础知识 本章所涉及的源代码文件名及位置 ·wireless.h external/ kernel-headers/ original/ linux/ wireless.h ·dri ...

  2. 《深入理解Android:Wi-Fi,NFC和GPS》章节连载[节选]--第三章 Wi-Fi基础知识

    首先感谢各位兄弟姐妹们的耐心等待.本书预计在3月中旬上市发售.从今天开始,我将在博客中连载此书的一些内容.注意,此处连载的是未经出版社编辑的原始稿件,所以样子会有些非专业. 注意,如下是本章目录,本文 ...

  3. WiFi基础知识讲解

    1.Wifi 基础知识: 常见名词 Ssid–WiFi名称 Sta-终端 AP–路由.无线交换机 LAN-LAN:指有线到有线(一般组网,或者打流的时候) LAN-WAN:指有线到无线(一般组网,或者 ...

  4. WIFI基础知识-2.4GZH的信号为什么比5GHZ的信号好

    WIFI基础知识-2.4GZH的信号为什么比5GHZ的信号好 其实如果你家里的路由器是双频路由,即同时支持2.4GHZ和5GHZ的信号.那么使用过程中,你可能会发现2.4GHZ的信号要比5GHZ的信号 ...

  5. 一,WIFI基础知识

    ** WI-FI ** wi-fi 基础知识 wi-fi零配过程鉴权分析 wi-fi设备接入天猫精灵AIoT平台 wi-fi设备Smart Config配网 wi-fi项目开发 WiFi简介 无线局域 ...

  6. 第四章 Android WiFi基础知识

    系列文章目录 第一章 国内下载AOSP最新源码的方法 第二章 下载AOSP WiFi相关的代码 第三章 将源码导入Android Studio(无需编译idegen) 文章目录 系列文章目录 前言 一 ...

  7. Linux 下wifi 驱动开发(一)—— WiFi基础知识解析

     一.WiFi相关基础概念 1.什么是wifi  我们看一下百度百科是如何定义的: Wi-Fi是一种可以将个人电脑.手持设备(如pad.手机)等终端以无线方式互相连接的技术,事实上它是一个高频无线电信 ...

  8. 【物联网】WiFi基础知识

    wifi的802.11协议中比较常见的知识做一个基本的总结和整理 一.基本概述 ============================ 1.有线和无线网络 目前有线网络中最著名的是以太网(Ethen ...

  9. 【物联网】WiFi基础知识 (一)【看评论区领取资料】

    wifi的802.11协议中比较常见的知识做一个基本的总结和整理 一.基本概述 ============================ 1.有线和无线网络 目前有线网络中最著名的是以太网(Ethen ...

  10. WIFI基础知识整理

    这里对wifi的802.11协议中比较常见的知识做一个基本的总结和整理,便于后续的学习.因为无线网络中涉及术语很多,并且许多协议都是用英文描述,所以有些地方翻译出来会有歧义,这种情况就直接英文来描述了 ...

最新文章

  1. shell脚本之case用法
  2. sql相同顺序法和一次封锁法_数据库:事务处理
  3. libgo 支持mysql,loadrunner通过使用libmysql.dll完成mysql的测试-Go语言中文社区
  4. 也来分析为什么支付宝要做社交
  5. python的程序格式框架_关于Python程序格式框架的描述,以下选项中错误的是
  6. leetcode917
  7. 谈一谈对旋转矩阵的理解
  8. OpenGL 坐标变换(1)
  9. 【李宏毅2020 ML/DL】P82 Generative Adversarial Network | Improving Sequence Generation by GAN
  10. webstorm注释写出的提示
  11. mac安装mysql(命令行方式)
  12. JAVA 油站管理系统_加油站管理系统
  13. android svc编解码,SVC(可分层视频编解码)技术详解
  14. Unity 优化Unity切换后台的问题
  15. 共识算法 —— PoA
  16. js内置对象【学习笔记】
  17. C# 调用 速印标签标准版 的模板lbl文件
  18. 如何在MAC OS中使用HAXM?
  19. 锚点定位 跳转到指定位置 回到顶部功能
  20. 晨曦记账本的功能有哪些

热门文章

  1. python读入图片,可视化展示图片
  2. validate简介及使用方法与默认的校验规则
  3. 如何在腾讯云搭建自己的网站
  4. 支持Linux系统双网卡ARM平台AM3352/AM3354
  5. uiniapp实现微信授权登录
  6. for update加锁
  7. dataframe_转换日期格式
  8. 通道注意力机制keras_注意力机制及Keras实现
  9. CSS3技巧:利用css3径向渐变做一张优惠券
  10. PIL库实现图像手绘效果