基于wpa_supplicant库的WIFI连接功能实现--wpa_cli命令代码改写

本文详细解析了如何使用wpa_cli命令在wpa_supplicant中执行操作,并通过代码示例展示了如何封装命令以实现统一管理。重点介绍了如何修改源码以获取命令执行结果,并通过实例说明了如何将多个命令封装为类的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇博客我们一起看了怎样使用wpas的命令后,接下来就利用这些命令来实现我们的代码。这些命令的实现都在wpa_cli.c文件中,以status命令为例,发生如下调用:

static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
    int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
    return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
}
wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
    _wpa_ctrl_command(ctrl, cmd, 1);
        char buf[2048];
        wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf,                                    
                                &len,wpa_cli_msg_cb);
        if (print)
            printf("%s", buf);

从上面可以看出,最终是调用wpa_ctrl_request函数完成命令,而最终结果是存在buf里,通过函数参数print来觉得是否输出到终端。所以我们最终需要取得的结果就是buf,只要拿到buf内容,我们就可以分析其内容实现我们需要的代码。为了实现上述目的,需要对源码进行下修改:我们把输出buf作为参数传入_wpa_ctrl_command函数,这样就可以拿到输出内容了。改写后代码如下:

static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf,  const char *cmd)
{
    return _wpa_ctrl_command(ctrl, buf, cmd, 0);
}
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf, const char *cmd, int print)
{
    char ret_buf[2048];
    int ret;
    size_t ret_len;

    ret_len = sizeof(ret_buf) - 1;
    ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), ret_buf, &ret_len,
                           wpa_cli_msg_cb);
    if (ret == -2)
    {
        printf("'%s' command timed out.\n", cmd);
        return -2;
    }
    else if (ret < 0)
    {
        printf("'%s' command failed.\n", cmd);
        return -1;
    }

    ret_buf[ret_len] = '\0';
    memcpy(buf, ret_buf, 2048);

    if (print)
    {
        ret_buf[ret_len] = '\0';
        printf("%s", ret_buf);
    }
    return 0;
}
static void wpa_cli_msg_cb(char *msg, size_t len)
{
    printf("%s\n", msg);
}

对于各个命令呢,我们同样需要加入buf,还是以status为例:

static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, char *buf,  int argc, char *argv[])
{
    int verbose = argc > 0 && strcmp(argv[0], "verbose") == 0;
    return wpa_ctrl_command(ctrl, buf, verbose ? "STATUS-VERBOSE" : "STATUS");
}

这样各个命令就达到了我们的目的。最后我们对命令进行一下封装,方便统一管理。

#define CFG_MAXARGS 10

/**
 * @brief 对wpa_supplicant中处理命令进行封装
 */
class HanleCmd : public QObject
{
    Q_OBJECT
public:
    HanleCmd();
    void printCmd();
    int hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd);

private:
    //命令封装
    struct wpa_cli_cmd
    {
        const char *cmd;  //命令名称
        int (*handler)(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);  //命令处理函数
    };

    QList<wpa_cli_cmd> listCmd;  //命令链表

    //私有函数
    void addCmd();
    int wpaRequest(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);
    int parseLine (char *line, char *argv[]);
};
HanleCmd::HanleCmd()
{
    qDebug("init cmd list");
    addCmd();
}
void HanleCmd::addCmd()
{
    struct wpa_cli_cmd cmd;

    //状态命令
    cmd.cmd = "status";
    cmd.handler = wpa_cli_cmd_status;
    listCmd.append(cmd);

    //扫描命令
    cmd.cmd = "scan";
    cmd.handler = wpa_cli_cmd_scan;
    listCmd.append(cmd);

    //扫描结果命令
    cmd.cmd = "scan_results";
    cmd.handler = wpa_cli_cmd_scan_results;
    listCmd.append(cmd);

    //选择AP命令
    cmd.cmd = "select_network";
    cmd.handler = wpa_cli_cmd_select_network;
    listCmd.append(cmd);

    //增加AP命令
    cmd.cmd = "add_network";
    cmd.handler = wpa_cli_cmd_add_network;
    listCmd.append(cmd);

    //列出配置文件中已经保存的AP信息
    cmd.cmd = "list_network";
    cmd.handler = wpa_cli_cmd_list_networks;
    listCmd.append(cmd);

    //设置AP
    cmd.cmd = "set_network";
    cmd.handler = wpa_cli_cmd_set_network;
    listCmd.append(cmd);

    //移除AP
    cmd.cmd = "remove_network";
    cmd.handler = wpa_cli_cmd_remove_network;
    listCmd.append(cmd);

    //使能某个AP
    cmd.cmd = "enable_network";
    cmd.handler = wpa_cli_cmd_enable_network;
    listCmd.append(cmd);

    //关闭某个AP
    cmd.cmd = "disable_network";
    cmd.handler = wpa_cli_cmd_disable_network;
    listCmd.append(cmd);

    //保存配置
    cmd.cmd = "save_config";
    cmd.handler = wpa_cli_cmd_save_config;
    listCmd.append(cmd);
}
int HanleCmd::parseLine (char *line, char *argv[])
{
    int nargs = 0;

    while (nargs < CFG_MAXARGS)
    {

        /* skip any white space */
        while ((*line == ' ') || (*line == '\t'))
        {
            ++line;
        }

        if (*line == '\0')      /* end of line, no more args    */
        {
            argv[nargs] = NULL;
            return (nargs);
        }

        argv[nargs++] = line;   /* begin of argument string */

        /* find end of string */
        while (*line && (*line != ' ') && (*line != '\t'))
        {
            ++line;
        }

        if (*line == '\0')      /* end of line, no more args    */
        {
            argv[nargs] = NULL;
            return (nargs);
        }

        *line++ = '\0';     /* terminate current arg     */
    }

    return (nargs);
}
int HanleCmd::hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd)
{
    int argc;
    char bufTmp[1024];
    char *argv[CFG_MAXARGS];

    strncpy(bufTmp, cmd, 1024);
    bufTmp[1023] = '\0';

    argc = parseLine(bufTmp, argv);
    return wpaRequest(ctrl, buf, argc, argv);
}

封装完毕后,只需要在执行某个命令时候调用如下代码即可:

char buf[2048];
handleCmd->hanleCmd(ctrl_conn, buf,  "status");

这样buf中存入的就是status命令的结果了。

至于参数中的ctrl_conn是底层的操作接口,需要读者自己去分析下wpa_cli的源码,这里只贴一下代码:

int WifiService::initWpa()
{
    qDebug()<<"initWpa";
    //1.变量初始化
    int conectNum = 0;
    ctrl_iface = NULL;
    ctrl_conn = NULL;
    monitor_conn = NULL;
    ctrl_iface_dir = strdup("/var/run/wpa_supplicant");

    //2.与wpa_supplicant建立连接
    while(true)
    {
        wpa_cli_get_default_ifname();
        if(ctrl_iface == NULL){
            qDebug("failed to wpa_cli_get_default_ifname");
            return -1;
        }

        wpa_cli_open_connection(ctrl_iface);
        if (ctrl_conn || monitor_conn){  //ActionThread未启用情况下只用ctrl_conn就可以
            qDebug("wpa_supplicant connection established");
            break;
        }else{
            if(conectNum++ >=2){  //最多试三次
                qDebug("wpa_supplicant connection established err");
                return -1;
            }
            qDebug("wpa_supplicant connection established err, we will try agin");
            usleep(10000);
            continue;
        }
    }
    return 0;
}
<think>我们正在解决“wpa_cli 无法连接wpa_supplicant”的问题,具体错误信息是:“Interactive mode Could not connect to wpa_supplicant wlan0 re-trying” 根据引用[1]和引用[2],以及我们已有的知识,我们可以按照以下步骤进行排查和解决。 注意:以下步骤需要root权限,请使用sudo或切换到root用户。 步骤1:检查wpa_supplicant进程是否存在 使用命令:`ps aux | grep wpa_supplicant` 如果存在,输出中会显示wpa_supplicant进程,并带有对应的接口(如-i wlan0)和配置文件(-c /path/to/config)。 如果不存在,则需要启动wpa_supplicant。 步骤2:启动wpa_supplicant(如果未运行) 根据引用[2]中的命令,我们可以这样启动: `wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -B` 注意:不同系统的配置文件路径可能不同,常见的路径有: - /etc/wpa_supplicant/wpa_supplicant.conf - /system/etc/wifi/wpa_supplicant.conf(Android系统) - /data/misc/wifi/wpa_supplicant.conf(Android系统) 步骤3:检查socket文件是否存在 引用[1]提到socket路径(如/data/misc/wifi/sockets/wlan0)是否存在。这个路径是wpa_supplicantwpa_cli通信的套接字文件。 在启动wpa_supplicant时,可以通过`-O`参数指定控制接口的路径(如`-O /var/run/wpa_supplicant`),如果没有指定,则使用默认路径。 默认路径可能因系统而异,常见的有: - /var/run/wpa_supplicant - /run/wpa_supplicant - /data/misc/wifi/sockets(Android) 检查对应接口的socket文件是否存在,例如: `ls /var/run/wpa_supplicant/wlan0` 步骤4:检查wpa_supplicant启动参数中的控制接口设置 在wpa_supplicant的配置文件(如/etc/wpa_supplicant/wpa_supplicant.conf)中,可以设置控制接口(ctrl_interface)和权限(ctrl_interface_group): ``` ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel ``` 确保配置文件中设置了控制接口,并且路径与步骤3中检查的路径一致。 步骤5:确保wpa_cli使用的socket路径正确 wpa_cli默认会尝试连接/var/run/wpa_supplicant/<interface>。如果wpa_supplicant的socket文件不在默认路径,可以通过环境变量`WPA_CTRL_DIR`指定目录,或者使用`-p`参数指定路径: `wpa_cli -i wlan0 -p /custom/path` 步骤6:权限问题 确保当前用户对控制接口的socket文件有读写权限。可以通过以下命令修改权限: `sudo chmod 777 /var/run/wpa_supplicant` (不推荐,仅用于测试) 或者,将当前用户加入控制接口配置中指定的组(例如GROUP=wheel),然后重启wpa_supplicant。 步骤7:如果以上步骤都无效,尝试重启网络服务 重启wpa_supplicant服务(如果系统使用systemd): `sudo systemctl restart wpa_supplicant` 或者,如果系统没有systemd,则手动结束进程再启动: `sudo killall wpa_supplicant` `sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -B` 步骤8:Android系统特殊处理 在Android系统中,wpa_supplicant通常由系统服务启动,并且socket文件位于/data/misc/wifi/sockets/。如果手动启动,需要确保以root用户运行,并且使用正确的配置文件和参数。 总结流程: 1. 检查wpa_supplicant进程 -> 不存在则启动。 2. 检查socket文件是否存在 -> 不存在则检查配置文件和启动参数。 3. 检查权限 -> 确保当前用户有权访问socket。 4. 尝试指定路径连接:`wpa_cli -i wlan0 -p /path/to/socket` 下面是一个具体的解决示例(以Linux系统为例):</think>### 解决 "Could not connect to wpa_supplicant" 错误 当出现 `Interactive mode Could not connect to wpa_supplicant wlan0 re-trying` 错误时,表明 `wpa_cli` 无法与 `wpa_supplicant` 守护进程通信。以下是系统级排查和解决方法: --- #### 🔍 **步骤 1:检查 wpa_supplicant 进程状态** ```bash ps aux | grep wpa_supplicant ``` - **预期输出**:应显示包含 `-i wlan0` 参数的进程(`wlan0` 替换为你的接口名) - **若无输出**:说明服务未运行,需手动启动(见步骤 2) --- #### ⚙️ **步骤 2:启动 wpa_supplicant 服务** ```bash sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -B ``` - **关键参数说明**: - `-i wlan0`:指定无线接口名 - `-c`:配置文件路径(常见路径见下表) - `-B`:后台运行模式 - **配置文件常见路径**: | 系统类型 | 配置文件路径 | |----------|--------------| | Linux | `/etc/wpa_supplicant/wpa_supplicant.conf` | | Android | `/system/etc/wifi/wpa_supplicant.conf` | | OpenWRT | `/var/run/wpa_supplicant-$INTERFACE.conf` | --- #### 📂 **步骤 3:检查 Socket 文件路径** ```bash sudo ls -l /var/run/wpa_supplicant/ # 或 Android 路径 sudo ls -l /data/misc/wifi/sockets/ ``` - **预期结果**:应存在接口对应的 socket 文件(如 `wlan0`) - **若文件不存在**: 1. 在配置文件中添加 socket 路径声明: ```ini ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel ``` 2. 重启服务: ```bash sudo pkill wpa_supplicant sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf -B ``` --- #### 🔐 **步骤 4:权限修复(关键步骤)** ```bash # 检查 socket 文件权限 sudo ls -l /var/run/wpa_supplicant/wlan0 # 修复权限(Linux 示例) sudo chown root:wheel /var/run/wpa_supplicant/wlan0 sudo chmod 660 /var/run/wpa_supplicant/wlan0 # Android 设备需 remount 分区 adb root adb remount ``` --- #### 🚨 **步骤 5:深度诊断** ```bash # 查看实时日志(需保持终端打开) sudo wpa_supplicant -d -i wlan0 -c /path/to/config.conf # 检查常见错误标志: # - "Failed to initialize control interface": socket 创建失败 # - "nl80211: Driver does not support authentication/association": 驱动问题 ``` --- #### 🛠️ **备选解决方案** 1. **强制重建 socket**: ```bash sudo rm /var/run/wpa_supplicant/wlan0 sudo systemctl restart wpa_supplicant ``` 2. **驱动重载**: ```bash sudo modprobe -r ath9k # 卸载驱动(ath9k 替换为你的驱动名) sudo modprobe ath9k # 重新加载 ``` 3. **DBus 模式替代方案**(若支持): ```bash sudo wpa_supplicant -i wlan0 -c config.conf -u ``` --- ### 💡 技术原理 错误根本原因是控制接口(ctrl_interface)通信中断,可能由以下情况导致: 1. `wpa_supplicant` 进程崩溃或被终止 2. Socket 文件权限错误(如 SELinux 限制) 3. 配置文件未声明 `ctrl_interface` 路径 4. 文件系统只读导致无法创建 socket(常见于 Android 系统分区) > 通过上述步骤,90% 的 `wpa_cli` 连接问题可被解决[^1][^2]。若问题持续,建议检查内核日志 `dmesg | grep wlan0` 排查驱动级错误。 --- ### 🔍 相关问题 1. 如何永久修复 Android 设备的 `wpa_supplicant` socket 权限问题? 2. 如何通过 `wpa_cli` 命令手动触发 Wi-Fi 重新认证? 3. `nl80211` 和 `wext` 驱动模式在 `wpa_supplicant` 中有何区别?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浓咖啡jy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值