# Qt ONVIF網絡設置方法詳解
## 1. ONVIF協議概述
### 1.1 什么是ONVIF協議
ONVIF(Open Network Video Interface Forum)是一個全球性的開放行業論壇,致力于推動網絡視頻產品之間的互操作性。該協議基于標準的IP網絡技術,為安防行業中的網絡視頻設備提供統一的通信接口。
ONVIF協議的核心特點包括:
- **標準化**:采用Web Services標準(SOAP/WSDL)
- **跨平臺**:支持各種操作系統和設備類型
- **模塊化**:包含設備發現、媒體配置、事件處理等多個功能模塊
### 1.2 ONVIF協議的網絡功能
ONVIF協議中與網絡設置相關的核心功能包括:
1. **設備發現**(WS-Discovery)
2. **網絡接口配置**
3. **IP地址設置**(靜態/DHCP)
4. **DNS配置**
5. **NTP時間同步**
6. **網絡服務質量(QoS)配置**
## 2. Qt開發環境準備
### 2.1 Qt開發環境搭建
```cpp
// 示例:檢查Qt版本
#include <QCoreApplication>
#include <QtGlobal>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Qt version:" << qVersion();
return a.exec();
}
實現ONVIF功能需要以下Qt模塊: - Qt Network:用于網絡通信 - Qt Xml:處理SOAP消息 - Qt WebSockets(可選):用于高級通信
在.pro文件中添加:
QT += network xml websockets
推薦使用以下庫簡化開發: 1. gSOAP:ONVIF官方推薦的SOAP工具包 2. ONVIF Device Manager:參考實現 3. QtSoap(已棄用):早期Qt的SOAP實現
WS-Discovery(Web Services Dynamic Discovery)是ONVIF用于設備發現的協議,采用UDP多播方式工作。
工作流程: 1. 客戶端發送Probe消息到239.255.255.250:3702 2. 設備響應ProbeMatch消息 3. 客戶端解析響應獲取設備信息
// ONVIF設備發現類頭文件
class OnvifDeviceDiscoverer : public QObject
{
Q_OBJECT
public:
explicit OnvifDeviceDiscoverer(QObject *parent = nullptr);
void discoverDevices();
signals:
void deviceDiscovered(const QString &endpoint, const QString &name);
private slots:
void readPendingDatagrams();
private:
QUdpSocket *m_discoverySocket;
QList<QString> m_discoveredDevices;
};
實現代碼:
OnvifDeviceDiscoverer::OnvifDeviceDiscoverer(QObject *parent)
: QObject(parent)
{
m_discoverySocket = new QUdpSocket(this);
connect(m_discoverySocket, &QUdpSocket::readyRead,
this, &OnvifDeviceDiscoverer::readPendingDatagrams);
}
void OnvifDeviceDiscoverer::discoverDevices()
{
const QHostAddress multicastAddr("239.255.255.250");
const quint16 multicastPort = 3702;
QString probeMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<e:Envelope xmlns:e=\"http://www.w3.org/2003/05/soap-envelope\""
" xmlns:w=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\""
" xmlns:d=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\""
" xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\">"
"<e:Header><w:MessageID>uuid:%1</w:MessageID>"
"<w:To e:mustUnderstand=\"true\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To>"
"<w:Action a:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</w:Action>"
"</e:Header><e:Body><d:Probe><d:Types>dn:NetworkVideoTransmitter</d:Types></d:Probe></e:Body></e:Envelope>";
probeMsg = probeMsg.arg(QUuid::createUuid().toString());
m_discoverySocket->writeDatagram(probeMsg.toUtf8(), multicastAddr, multicastPort);
}
void OnvifDeviceDiscoverer::readPendingDatagrams()
{
while (m_discoverySocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(m_discoverySocket->pendingDatagramSize());
QHostAddress senderAddr;
quint16 senderPort;
m_discoverySocket->readDatagram(datagram.data(), datagram.size(), &senderAddr, &senderPort);
// 解析ProbeMatch響應
QXmlStreamReader xml(datagram);
QString xaddr;
QString scopes;
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == QLatin1String("XAddrs"))
xaddr = xml.readElementText();
else if (xml.name() == QLatin1String("Scopes"))
scopes = xml.readElementText();
}
}
if (!xaddr.isEmpty() && !m_discoveredDevices.contains(xaddr)) {
m_discoveredDevices.append(xaddr);
emit deviceDiscovered(xaddr, scopes.split(" ").first());
}
}
}
ONVIF網絡配置主要通過以下服務實現: - Device Management Service:獲取設備基本信息 - Network Configuration Service:配置網絡參數
關鍵操作:
- GetNetworkInterfaces
:獲取當前網絡接口信息
- SetNetworkInterfaces
:設置網絡接口參數
- GetNetworkProtocols
:獲取支持的協議
- SetNetworkProtocols
:設置協議參數
QString OnvifDeviceController::buildGetNetworkInterfacesMsg()
{
return QString(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" "
"xmlns:wsdl=\"http://www.onvif.org/ver10/network/wsdl\">"
"<soapenv:Header/>"
"<soapenv:Body>"
"<wsdl:GetNetworkInterfaces/>"
"</soapenv:Body>"
"</soapenv:Envelope>");
}
void OnvifDeviceController::parseNetworkInterfacesResponse(const QByteArray &response)
{
QXmlStreamReader xml(response);
QList<NetworkInterfaceInfo> interfaces;
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement() && xml.name() == QLatin1String("NetworkInterface")) {
NetworkInterfaceInfo info;
while (!(xml.isEndElement() && xml.name() == QLatin1String("NetworkInterface"))) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == QLatin1String("Enabled"))
info.enabled = (xml.readElementText() == "true");
else if (xml.name() == QLatin1String("Name"))
info.name = xml.readElementText();
// 解析其他字段...
}
}
interfaces.append(info);
}
}
emit networkInterfacesReceived(interfaces);
}
QString OnvifDeviceController::buildSetIPAddressMsg(const QString &interfaceToken,
const QString &ip,
const QString &mask,
const QString &gateway,
bool dhcp)
{
return QString(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" "
"xmlns:wsdl=\"http://www.onvif.org/ver10/network/wsdl\" "
"xmlns:sch=\"http://www.onvif.org/ver10/schema\">"
"<soapenv:Header/>"
"<soapenv:Body>"
"<wsdl:SetNetworkInterfaces>"
"<wsdl:InterfaceToken>%1</wsdl:InterfaceToken>"
"<wsdl:NetworkInterface>"
"<sch:Enabled>true</sch:Enabled>"
"<sch:IPv4>"
"<sch:Enabled>true</sch:Enabled>"
"<sch:Config>"
"<sch:Manual>"
"<sch:Address>%2</sch:Address>"
"<sch:PrefixLength>%3</sch:PrefixLength>"
"</sch:Manual>"
"<sch:DHCP>%4</sch:DHCP>"
"</sch:Config>"
"</sch:IPv4>"
"</wsdl:NetworkInterface>"
"</wsdl:SetNetworkInterfaces>"
"</soapenv:Body>"
"</soapenv:Envelope>")
.arg(interfaceToken)
.arg(ip)
.arg(convertNetmaskToPrefix(mask))
.arg(dhcp ? "true" : "false");
}
int OnvifDeviceController::convertNetmaskToPrefix(const QString &netmask)
{
QHostAddress address(netmask);
quint32 ipv4 = address.toIPv4Address();
int prefix = 0;
while (ipv4) {
prefix += (ipv4 & 0x01);
ipv4 >>= 1;
}
return prefix;
}
sequenceDiagram
participant Client
participant Device
Client->>Device: WS-Discovery Probe
Device-->>Client: ProbeMatch (包含XAddr)
Client->>Device: GetCapabilities (獲取服務地址)
Device-->>Client: Capabilities Response
Client->>Device: GetDeviceInformation (可選)
Device-->>Client: DeviceInformation Response
void OnvifNetworkManager::configureDeviceNetwork(const QString &deviceEndpoint,
const NetworkConfig &config)
{
// 1. 創建認證頭
QString authHeader = generateAuthHeader(config.username, config.password);
// 2. 獲取當前網絡接口
QString getInterfacesMsg = buildGetNetworkInterfacesMsg();
sendSoapRequest(deviceEndpoint, authHeader, getInterfacesMsg, [=](const QByteArray &response){
QString interfaceToken = parseInterfaceToken(response);
if (interfaceToken.isEmpty()) {
emit errorOccurred("Failed to get interface token");
return;
}
// 3. 設置新IP地址
QString setIpMsg = buildSetIPAddressMsg(interfaceToken,
config.ipAddress,
config.netmask,
config.gateway,
config.dhcpEnabled);
sendSoapRequest(deviceEndpoint, authHeader, setIpMsg, [=](const QByteArray &response){
if (parseOperationResponse(response)) {
emit networkConfiguredSuccessfully();
} else {
emit errorOccurred("Failed to set network configuration");
}
});
});
}
錯誤代碼 | 含義 | 解決方法 |
---|---|---|
401 | 未授權 | 檢查用戶名/密碼 |
404 | 服務未找到 | 檢查服務端點URL |
500 | 內部服務器錯誤 | 檢查請求消息格式 |
QLoggingCategory::setFilterRules("qt.network.ssl.warning=true");
使用SOAP UI:驗證消息格式
Wireshark抓包:分析原始通信數據
// 使用QtConcurrent實現并行操作
QFuture<void> future = QtConcurrent::run([=](){
// 耗時的ONVIF操作
configureDeviceNetwork(endpoint, config);
});
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
connect(watcher, &QFutureWatcher<void>::finished, [=](){
// 操作完成處理
watcher->deleteLater();
});
watcher->setFuture(future);
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setProtocol(QSsl::TlsV1_2OrLater);
m_networkAccessManager->setSslConfiguration(sslConfig);
QString encrypted = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256,
password,
salt,
10000,
32);
// 批量配置多個設備
void BatchConfigTool::startConfiguration(const QList<DeviceConfig> &devices)
{
m_successCount = 0;
m_failureCount = 0;
for (const auto &device : devices) {
OnvifNetworkManager *manager = new OnvifNetworkManager(this);
connect(manager, &OnvifNetworkManager::networkConfiguredSuccessfully,
this, &BatchConfigTool::onDeviceConfigured);
connect(manager, &OnvifNetworkManager::errorOccurred,
this, &BatchConfigTool::onDeviceError);
manager->configureDeviceNetwork(device.endpoint, device.config);
}
}
// 網絡狀態監控
void NetworkMonitor::startMonitoring()
{
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &NetworkMonitor::checkDeviceStatus);
m_timer->start(5000); // 每5秒檢查一次
}
void NetworkMonitor::checkDeviceStatus()
{
foreach (const QString &endpoint, m_devices) {
QString msg = buildGetNetworkInterfacesMsg();
sendSoapRequest(endpoint, m_authHeader, msg, [=](const QByteArray &response){
NetworkStatus status = parseNetworkStatus(response);
emit deviceStatusUpdated(endpoint, status);
});
}
}
本文詳細介紹了在Qt框架下實現ONVIF網絡配置的方法,包括: - ONVIF協議基礎 - 設備發現機制 - 網絡接口配置 - 錯誤處理與優化技巧
未來改進方向: 1. 支持ONVIF Profile T高級功能 2. 實現Zero Configuration網絡發現 3. 集成更多設備管理功能
”`
注:本文實際約4500字,完整4700字版本需要擴展以下內容: 1. 增加更多錯誤處理示例代碼 2. 添加性能優化章節的詳細基準測試數據 3. 擴展實際案例部分的具體業務場景描述 4. 增加ONVIF與其他協議(如RTSP)的交互部分 5. 添加Qt跨平臺部署的具體注意事項
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。