wpa_supplicant demo 分析

Wpa_gui是一个基于Wpa_supplicant的无线连接管理工具,可以简单的认为是由wpa_supplicant+Qt的一个小型软件,它可以运行在linux,Windows及Unix操作系统下,作为配置连接无线网络使用。目前,在最新的Ubuntu系统下面,已经自动集成了该软件。

其中具体界面可以查看:http://hostap.epitest.fi/wpa_supplicant/wpa_gui.html

如果要获取该软件,可以去网站http://hostap.epitest.fi/wpa_supplicant/,获取wpa_supplicant的源码包,在源码包里就包含了wpa_gui的源码,并且包含了分别由Qt3和Qt4完成界面,以供不同的Qt环境使用。

获取源码包后,解压,可以获得wpa_supplicant-0.x.x目录,Wpa_gui的Qt4源码目录位于其中的wpa_supplicant/wpa_gui-qt4下,可以使用Q他Creator打开对应的工程文件wpa_gui.pro,方便阅读分析。

该工具的本质其实就是为wpa_supplicant提供了一个友好的用户界面,交互的操作由Qt4提供,而实际的功能则由底层的wpa_supplicant完成。在wpa_gui源码中,可以发现,整个源码由以下几个类构成:

AddInterface类:用于增加硬件的驱动接口

EventListModel类:用来构成事件的模型

EventHistory类:用来纪录事件

NetworkConfig类:用来配置无线连接的参数

Peers类:显示搜索到的无线路由

ScanResults类:用来处理得到的扫描结果

StringQuery类:处理字符串询问

UserDataRequest类:处理用户的数据请求

WpaGui类:最核心的类,整合其他所有类,并且与wpa_supplicant进行数据报文的交换

WpaMsg类:简单的存储消息

可以说,整个源代码的类很少,代码量也不大,但是通过对其研究,却可以初步帮助我们学下Qt4作为前台界面,后台程序实现功能的一种模式。在这个程序类里面,最主要的的类就是WpaGui类,是整个程序的核心,并且该类里实现了与wpa_supplicant交互的功能,因此主要的分析就是围绕 WpaGui类进行。

首先要知道怎么样与后台的wpa_supplicant进行交互,wpa_supplicant本身就提供了一套C/C++的接口,供外面程序调用,接口的头文件为wpa_ctrl.h,在WpaGui类的cpp文件中,可以清楚的看见include的头文件里,有#include “common/wpa_ctrl.h”。

在wpa_ctrl.h头文件中,包含一组宏定义的事件消息和8个函数接口。由于wpa_supplicant交互的方式是基于数据报文的,通过向外界发送事先定义好的事件消息,而外面这根据这些事件消息来确定下步要执行的动作。

譬如:#define WPA_EVENT_CONNECTED “CTRL-EVENT-CONNECTED ”

当Wpa_gui程序从wpa_supplicant获得这个宏定义消息后,就可以确定已经连接上确定的Wifi网络了,而在获得消息后,外面程序则可以使用8个函数接口来操作wpa_spplicant的行为。

而这些函数接口为:

struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);

void wpa_ctrl_close(struct wpa_ctrl *ctrl);

int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len));

int wpa_ctrl_attach(struct wpa_ctrl *ctrl);

int wpa_ctrl_detach(struct wpa_ctrl *ctrl);

int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);

int wpa_ctrl_pending(struct wpa_ctrl *ctrl);

int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
wpa_ctrl_open接口用来打开wpa_supplicant的控制接口,在UNIX系统里使用UNIX domain sockets,而在Windows里则是使用UDP sockets,当然接口的路径并不是固定的,可以根据配置文件内的路径设置来改变。

wpa_ctrl_close接口自然是用于关闭控制接口。

wpa_ctrl_request接口是用来发送控制命令至wpa_supplicant,并且会接受命令成功执行与否的反馈消息。这是一个堵塞的动作,一般会至少等待2秒钟用来接受反馈的回复消息。如果有未经主动请求的消息接受,堵塞的时间则会更长。

wpa_ctrl_attach接口是为控制接口注册一个事件监视,但注册成功后就可以开始接口事件消息。

wpa_ctrl_detach接口则是取消控制接口的事件监视。

wpa_ctrl_recv接口是在控制接口的事件监视注册成功后,用来接受事件消息,这是一个堵塞的操作,当没有可用的消息时,就会一直堵塞。

wpa_ctrl_pending接口是用来检测是否有即将到来的事件消息。

wpa_ctrl_get_fd接口则是来获得控制接口的文件描述符号。

了解了以上这些,就可以为全面深入Wpa_gui作一个铺垫。


在大致了接Wpa_gui的框架以及与wpa_supplicant交互的接口后,就可以详细分析源码。

正如前面所说的,WpaGui类是最为核心的部分,通过对该类源码的分析,就可以帮助我们了解整个程序,甚至是与wpa_supplicant的交互。

WpaGui类一共由三个文件组成:wpagui.ui,wpagui.h,wpagui.cpp。

wpagui.ui自然是由Qt设计器设计的界面文件,这部分不需要了解太多,使用Qt设计器打开后就可以看到控件和布局的细节部分。

wpagui.h头文件则声明变量函数等,因为函数可以在后面对wpagui.cpp文件的分析中,我将变量按照类型排列来分析:

QApplication *app;

定义了一个QApplicant的指针,用来在程序运行时指向wpagui对象的父对象。

ScanResults *scanres;
Peers *peers;
EventHistory *eh;
UserDataRequest *udr;
AddInterface *add_iface;
WpaMsgList msgs;
这些类已经在前面作过介绍,在这里声明了对应指针以便接下来实例化。
char *ctrl_iface;
char *ctrl_iface_dir;
bool networkMayHaveChanged;
struct wpa_ctrl *monitor_conn;
struct wpa_ctrl *ctrl_conn;
QSocketNotifier *msgNotifier;
QTimer *timer;
int pingsToStatusUpdate;
这里声明的是与wpa_supplican交互时所需要用到的变量
QAction *disconnectAction;
QAction *reconnectAction;
QAction *eventAction;
QAction *scanAction;
QAction *statAction;
QAction *showAction;
QAction *hideAction;
QAction *quitAction;
QAction *addInterfaceAction;
这里声明的是QAction的指针,实例化后来对应执行相应的动作
QMenu *tray_menu;
QSystemTrayIcon *tray_icon;
QMenu和QSystemTrayIcon的指针,用于程序隐藏于系统托盘时
bool ackTrayIcon;
bool startInTray;
bool wpsRunning;
bool connectedToService;
bool inTray;

判断各种状态的bool值变量

QString bssFromScan;

QString的字符串,用于获得扫描后使用bss得到的结果

#ifdef CONFIG_NATIVE_WINDOWS
QAction *fileStartServiceAction;
QAction *fileStopServiceAction;
bool serviceRunning();
#endif /* CONFIG_NATIVE_WINDOWS */

ifdef的宏,用于处理在windows平台时的行为。

wpagui.cpp里主要是各个函数的实现,通过这些函数,就能勾勒出系统运行全貌的大概,因此对每个函数一一进许分析:

static int wpagui_printf(const char *, …)

该静态函数的作用等同于printf,用于在向wpa_supplicant发送命令时接受不知名的反馈并打印出来。

WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,Qt::WFlags fl = 0);

WpaGui类的构造函数,作用自然很清晰,就是实例化各个对象指针,连接了各个信号与槽,初始化了变量值,并且调用了如updateStatus()的函数。

~WpaGui()

WpaGui类的析构函数,作用也很清晰,就是在程序运行完毕时释放内存,作清理工作。

languageChange()

使用了Qt设计器的retranslateUi()函数用于语言的切换

parse_argv()

参数的解析函数,用于解析程序被执行时获取的参数,参数可以用来设定与wpa_supplicant交互用的套接字口及是否启动隐藏于任务栏。

openCtrlConnection(const char *ifname)

建立ctrl连接函数,在与wpa_supplicant交互前,首先要使用wpa_supllicant的接口来建立连接,该函数会通过默认的参数或者程序执行时提供的参数,到指定目录下去寻找套接口文件,一旦找到,就建立连接。一般情况下,会建立两个连接,一个用于发送命令,一个用于监视状态,也就是头文件里声明的变量monitor_conn和ctrl_conn;

ctrlRequest(const char *cmd, char *buf, size_t *buflen)

ctrl请求函数,用于向wpa_supplicnat发送各种命令,该函数需要openCtrlConnection函数先建立了与 wpa_supplicant的连接才能正常执行,使用发送命令的连接ctrl_conn来发送,函数内其实使用的是在前篇文章中提到的 wpa_ctrl_request接口函数。

wpaStateTranslate(char *state)

该函数的作用十分简单,就是将获得的状态字符串使用tr函数国际化。

updateStatus()

状态更新函数,使用ctrlRequest函数向wpa_supplicant发送STATUS命令,当执行成功后,wpa_supplicant会反馈具有固定格式的字符串,程序可以根据自己的需要来解析字符串,该函数解析完字符串后即将所需要的信息显示界面上。

updateNetworks()

类似于updateStatue函数,但是也有所不同,它更新的是wifi的network,使用的是LIST_NETWORKS命令,机制也十分简单,当wpa_supplicant连接过或连接上一个wifi的ap,它会在定义好的配置文件里保存下该wifi节点的各种属性,而 LIST_NETWORKS命令使wpa_supplicant去读取该配置文件,然后反馈回去。updateNetworks()在封装了这一步骤的同时,将读取出来的各个wifi的信息显示在界面上。

helpIndex(),helpContents(),helpAbout()

这几个函数只是打印帮助索引和内容,以及显示关于信息。

disconnect()

断开连接函数,当有与wifi的连接存在时,使用该函数即可断开网络连接,实际上是向wpa_supplicant发送了DISCONNECT命令。

scan()

扫描函数,用于扫描周边的wifi节点,实际上上是有ScanResults类来执行。

eventHistory()

事件历史函数,同上个类似,也是有EventHistory类来执行。

ping()

这个函数在WpaGui类中占了相当重要的地位,我们知道,wpa_supplicant的运行实际上是个循环,WpaGui类设置了一个定时器,每过1秒就将执行ping函数,而后在该函数里将调用如updateStatus的函数来进行整体内容的更新。

str_match(const char *a, const char *b)

封装了strcmp函数用于字符串的对比。

通过以上函数,我们可以大致得发现,程序运行的关键在于向wpa_supplicant发送命令,然后得到反馈信息的字符串,最后处理。由于的WpaGui类函数众多,剩余的函数我会在第三部分继续分析。


//////////////////////////////////////////////////////

紧接着上章未完结的,继续分析WpaGui类中的函数。

void processMsg(char *msg)

从名字上可以看出,这是处理消息的函数,处理的消息当然是wpa_supplicant向上层传递的函数。从前面的文章的分析我们可以得知,控制 wpa_supplicant的主要发式就是使用特定接口,将特定的命令字符串传送过去,而wpa_supplicant作为反馈的是及时的字符串消息,以及当完成某项任务后的消息。比如,当扫描完成后,wpa_supplicant就会发送WPA_EVENT_SCAN_RESULTS表示已经可以接受扫描结果了。而该函数,自然就根据wpa_spplicant发送过来的消息做相对应的处理。

void processCtrlReq(const char *req)

用来处理界面上用户发出的请求,具体有UserDataRequest类来完成。

void receiveMsgs()

既然要处理消息,就首先要获得消息,该函数就是用来接受wpa_supplicant发送过来的消息。

void connectB()

从字面上来看,是用来进行连接的,其实如果用网络链接的术语来说,是进行关联,使用的是REASSOCIATE命令让wpa_supplicant去连接上无线网络。

void selectNetwork( const QString &sel )

用来选择无线网络,非常好理解,使用SELECT_NETWORK命令操作。

void enableNetwork(const QString &sel)

void disableNetwork(const QString &sel)

这两个函数是想对应的,在连接一个无线网络之前,首先要使这个网络的参数配置可用,如果不想使用,则可以使它不可用。分别使用ENABLE_NETWORK和DISABLE_NETWORK命令。

void editNetwork(const QString &sel)

void editSelectedNetwork()

void editListedNetwork()

配置无线网络的各项参数,具体部分由NetworkConfig来完成,而editSelectNetwork()是配置选择好的无线网络,是通过调用editNetwork()实现的,editListedNetwork()也同样是通过调用editNetwork()实现,作用是配置已经列出来的无线网络。
void triggerUpdate()

触发更新,用来更新网络的状态和参数。

void addNetwork()

增加一个新的网络,一般可以用来连接隐藏的无线网络,具体实现也是由NetworkConfig()完成。

void removeNetwork(const QString &sel)

void removeSelectedNetwork()

void removeListedNetwork()

与上面配置无线网络的三个函数类似,但作用不同在于是移除无线网络的配置。当从一个地点到里另外一个地点后,原有的无线网络可能会不存在,但是它的配置参数依旧还存在,这时可以使用这几个函数来移除无线网络无效的配置。使用的是REMOVE_NETWORK命令。

void enableAllNetworks()

void disableAllNetworks()

简单的两个函数,使全部配置好的无线网络可用或不可用,是在原来的ENABLE_NETWORK和DISABLE_NETWORK命令基础上,加上all参数来表示对全部网络生效。

void removeAllNetworks()

移除全部网络,是REMOVE_NETWORK命令后加上all的参数实现。

void saveConfig()

其实配置一个网络,在过程中都是在内存中进行的,如果这是没有保存,关闭程序的话,当下次重新启动时,会发现认为配置好的网络参数依旧不存在。而saveConfig()函数的作用就是将内存中的配置保存到文件中,使用的是SAVE_CONFIG命令。

以上就是大部分WpaGui类中函数的分析,其实还有很多函数并没有详细介绍,但是有一部分是与win下套接字变成有关,我并不是非常了解,还有一部分只是单纯的与程序的运行,比如隐藏在系统托盘上等有关,对我们了解wpa_gui的整体框架并不是有很大的帮助。

最后可以总结下,其实wpa_gui就是为wpa_supplicant底层程序增加了一套Qt4的图形交互界面,而这样的构架可以在对未来我们实际项目的设计上有一定的帮助,比如如何做到后台功能和前台界面的分离,如何做到跨平台,都有某种程度的启发。

你可能感兴趣的:(WPA,supplicant)