QT实现linux底层设备监听(netlink)
一、简介一些与硬件设备相关的软件需要监听设备的热插拔、断开、连接情况,本文介绍了QT中netlink实现方式。大致思路:linux设备改变会触发相关事件,使用netlink与linux内核空间通信,再使用QSocketNotifier监听,绑定Qt槽函数实现:netlink(Linux通信) + QSocketNotifier功能:网卡监听、串口监听优点:监听串口能快速响应缺点:监听网卡有些迟钝n
·
一、简介
一些与硬件设备相关的软件需要监听设备的热插拔、断开、连接情况,本文介绍了QT中netlink实现方式。
大致思路:linux设备改变会触发相关事件,使用netlink与linux内核空间通信,再使用QSocketNotifier监听,绑定Qt槽函数
- 实现:netlink(Linux通信) + QSocketNotifier
- 功能:网卡监听、串口监听
- 优点:监听串口能快速响应
- 缺点:监听网卡有些迟钝
netlink介绍:
Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。
在Linux 内核中,使用netlink 进行应用与内核通信的应用有很多,如
- 路由 daemon(NETLINK_ROUTE)
- 用户态 socket 协议(NETLINK_USERSOCK)
- 防火墙(NETLINK_FIREWALL)
- netfilter 子系统(NETLINK_NETFILTER)
- 内核事件向用户态通知(NETLINK_KOBJECT_UEVENT) 本文使用
- 通用netlink(NETLINK_GENERIC)
QSocketNotifier介绍:
用来监听系统文件操作,将操作转换为Qt事件进入系统的消息循环队列。并调用预先设置的事件接受函数,处理事件。
参考:QSocketNotifier Class | Qt Core 5.15.7
定时器的实现方式见我另一篇博客:QT实现linux底层设备监听(QTimer)_俊俊的博客-CSDN博客
二、效果
启动软件,显示当前已经存在的设备

当有设备插入、拔出或者连接、断开时提示
拔出串口设备

三、实现
devicemonitor.h
#ifndef QDEVICEWATCHER_P_H
#define QDEVICEWATCHER_P_H
#include <QBuffer>
#include <QList>
#include <QThread>
#include <QDebug>
#include <QSerialPortInfo>
#include <QNetworkInterface>
class QDeviceWatcherPrivate: public QObject
{
Q_OBJECT
public:
QDeviceWatcherPrivate(QObject *parent = 0);
~QDeviceWatcherPrivate();
bool start();
bool stop();
QList<QString> getSerialPortNames();
QList<QString> getNetworkNames();
//QList<QObject*> event_receivers;
signals:
void deviceChanged();
private slots:
void parseDeviceInfo();
private:
bool init();
QBuffer buffer;
void parseLine(const QByteArray& line);
class QSocketNotifier *socket_notifier;
int netlink_socket;
QList<QString> _serialPortNames;
QList<QString> _networkNames;
};
#endif // QDEVICEWATCHER_P_H
devicemonitor.cpp
#include "devicemonitor.h"
#ifdef Q_OS_LINUX
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#else
#endif
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <unistd.h>
#include <QtCore/QCoreApplication>
#include <qregexp.h>
#include <QSocketNotifier>
#define UEVENT_BUFFER_SIZE 2048
enum udev_monitor_netlink_group {
UDEV_MONITOR_NONE,
UDEV_MONITOR_KERNEL,
UDEV_MONITOR_UDEV
};
QDeviceWatcherPrivate::QDeviceWatcherPrivate(QObject *parent)
: QObject(parent)
{
_serialPortNames = getSerialPortNames();
_networkNames = getNetworkNames();
qDebug()<< "初始化设备";
qDebug()<< "串口:" + _serialPortNames.join(",");
qDebug()<< "网卡:" + _networkNames.join(",");
}
QDeviceWatcherPrivate::~QDeviceWatcherPrivate()
{
stop();
close(netlink_socket);
netlink_socket = -1;
}
bool QDeviceWatcherPrivate::start()
{
if (!init())
return false;
socket_notifier->setEnabled(true);
return true;
}
bool QDeviceWatcherPrivate::stop()
{
if (netlink_socket!=-1) {
socket_notifier->setEnabled(false);
close(netlink_socket);
netlink_socket = -1;
}
return true;
}
QList<QString> QDeviceWatcherPrivate::getSerialPortNames()
{
QList<QString> serialPortNames;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
serialPortNames.append(info.portName());
}
return serialPortNames;
}
QList<QString> QDeviceWatcherPrivate::getNetworkNames()
{
QList<QString> networkNames;
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
foreach (QNetworkInterface interfaceItem, list) {
if (!interfaceItem.isValid())
continue;
QList<QNetworkAddressEntry> addressEntryList = interfaceItem.addressEntries();
foreach(QNetworkAddressEntry addressEntryItem, addressEntryList)
{
if(addressEntryItem.ip().protocol()==QAbstractSocket::IPv4Protocol&&
addressEntryItem.ip().toString().left(3)=="192")
{
networkNames.append(interfaceItem.name());
}
}
}
return networkNames;
}
bool QDeviceWatcherPrivate::init()
{
struct sockaddr_nl snl;
const int buffersize = 16 * 1024 * 1024;
int retval;
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_groups = UDEV_MONITOR_KERNEL;
netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (netlink_socket == -1) {
qWarning("error getting socket: %s", strerror(errno));
return false;
}
/* set receive buffersize */
setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
retval = bind(netlink_socket, (struct sockaddr*) &snl, sizeof(struct sockaddr_nl));
if (retval < 0) {
qWarning("bind failed: %s", strerror(errno));
close(netlink_socket);
netlink_socket = -1;
return false;
} else if (retval == 0) {
//from libudev-monitor.c
struct sockaddr_nl _snl;
socklen_t _addrlen;
/*
* get the address the kernel has assigned us
* it is usually, but not necessarily the pid
*/
_addrlen = sizeof(struct sockaddr_nl);
retval = getsockname(netlink_socket, (struct sockaddr *)&_snl, &_addrlen);
if (retval == 0)
snl.nl_pid = _snl.nl_pid;
}
socket_notifier = new QSocketNotifier(netlink_socket, QSocketNotifier::Read, this);
connect(socket_notifier, SIGNAL(activated(int)), SLOT(parseDeviceInfo())); //will always active
socket_notifier->setEnabled(false);
return true;
}
void QDeviceWatcherPrivate::parseDeviceInfo()
{
/*
问题记录:串口热插拔能够快速反应,但网卡反应比较迟钝
*/
// zDebug("%s active", qPrintable(QTime::currentTime().toString()));
QByteArray data;
data.resize(UEVENT_BUFFER_SIZE*2);
data.fill(0);
size_t len = read(socket_notifier->socket(), data.data(), UEVENT_BUFFER_SIZE*3);
// qDebug("read fro socket %d bytes", len);
data.resize(len);
data = data.replace(0, '\n').trimmed(); //In the original line each information is seperated by 0
if (buffer.isOpen())
buffer.close();
buffer.setBuffer(&data);
buffer.open(QIODevice::ReadOnly);
while(!buffer.atEnd()) { //buffer.canReadLine() always false?
parseLine(buffer.readLine().trimmed());
}
buffer.close();
}
void QDeviceWatcherPrivate::parseLine(const QByteArray &line)
{
// qDebug()<< line.constData();
// if (line.contains("remove@") || line.contains("add@")) //寻找关键词
// {
// emit deviceChanged();
// }
QList<QString> serialPortNames = getSerialPortNames();
QList<QString> networkNames = getNetworkNames();
if(_serialPortNames != serialPortNames)
{
if (serialPortNames.length() > _serialPortNames.length())
{
_serialPortNames = serialPortNames;
qDebug() << "串口插入:" + _serialPortNames.join(",");
emit deviceChanged();
}
if (serialPortNames.length() < _serialPortNames.length())
{
_serialPortNames = serialPortNames;
qDebug() << "串口拔出:" + _serialPortNames.join(",");
emit deviceChanged();
}
}
if(_networkNames != networkNames)
{
if(networkNames.length() > _networkNames.length())
{
_networkNames = networkNames;
qDebug() << "网卡连接:" + _networkNames.join(",");
emit deviceChanged();
}
if(networkNames.length() < _networkNames.length())
{
_networkNames = networkNames;
qDebug() << "网卡断开:" + _networkNames.join(",");
emit deviceChanged();
}
}
}
#endif //Q_OS_LINUX
使用
//#include "mainwindow.h"
#include "devicemonitor.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// MainWindow w;
// w.show();
QDeviceWatcherPrivate *watcher = new QDeviceWatcherPrivate;
watcher->start();
return a.exec();
}
更多推荐



所有评论(0)