编译环境QT4.8-ARM-V500
用华为荣耀路由器测试成功,代码源自github,有修改
/*"QUpnpPortMapper.h"*/
#ifndef QUPNPPORTMAPPER_H
#define QUPNPPORTMAPPER_H
#include <QObject>
#include <QHostAddress>
#include <QUdpSocket>
#include <QTimer>
#include <QNetworkAccessManager>
#include <QDomElement>
#include <QUrl>
class QUpnpPortMapper : public QObject
{
Q_OBJECT
signals:
void discoverFinished(bool ok);
void queryExternalAddressFinished(QHostAddress address, bool ok, QString errorString);
void addPortMappingFinished(bool ok, QString errorString);
void deletePortMappingFinished(bool ok, QString errorString);
public:
QUpnpPortMapper(QObject *parent = 0);
~QUpnpPortMapper();
QHostAddress localAddress();
bool isDiscoverOk();
public slots:
bool open(const QHostAddress & localAddress);
bool open(const QNetworkInterface & networkInterface);
void close();
bool discover(int timeout = 5000, bool wait = false);
QHostAddress queryExternalAddress(bool wait = false);
bool addPortMapping(QAbstractSocket::SocketType type, QHostAddress internalAddress, quint16 internalPort, quint16 externalPort, QString description = QString(), bool wait = false);
bool deletePortMapping(QAbstractSocket::SocketType type, quint16 externalPort, bool wait = false);
private slots:
void onDiscoverTimeout();
void onUdpSocketReadyRead();
void my_onUdpSocketReadyRead();
void onReplyFinished(QNetworkReply * reply);
void onReplyDiscover(QNetworkReply * reply);
void onReplyQueryExternalAddress(QNetworkReply * reply);
void onReplyAddPortMapping(QNetworkReply * reply);
void onReplyDeletePortMapping(QNetworkReply * reply);
private:
QString recursiveFindControlUrl(const QDomElement & parent);
QString getXmlTabContent(QString content, QString tab, Qt::CaseSensitivity cs);
private:
QHostAddress m_localAddress;
QUdpSocket * m_udpSocket;
QTimer * m_timer;
QNetworkAccessManager * m_networkAccessManager;
QUrl m_controlUrl;
bool m_discoverOk;
QHostAddress m_lastExternalAddress;
bool m_lastAddPortMappingOk;
bool m_lastDeletePortMappingOk;
};
#endif // QUPNPPORTMAPPER_H
/*"QUpnpPortMapper.cpp"*/
#include "QUpnpPortMapper.h"
#include <QtDebug>
#include <QNetworkInterface>
#include <QDomDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QRegExp>
#include <QStringList>
#include <stdio.h>
/*WANIPConnection*/
static const unsigned char raw_discover[] = {
0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0xd,0xa/*ST*/,
0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
0xd,0xa,/*end*/
};
/*WANPPPConnection*/
static const unsigned char raw_ppp_discover[] = {
0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x50,0x50,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0xd,0xa,/*WAN PPPConnection*/
0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
0xd,0xa,/*end*/
};
/*IGD*/
static const unsigned char raw_igd_discover[] = {
0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x64,0x65,0x76,0x69,0x63,0x65,0x3a,0x49,0x6e,0x74,0x65,0x72,0x6e,0x65,0x74,0x47,0x61,0x74,0x65,0x77,0x61,0x79,0x44,0x65,0x76,0x69,0x63,0x65,0x3a,0x31,0xd,0xa,/*InternetGatewayDevice*/
0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
0xd,0xa,/*end*/
};
static const unsigned char raw_queryExternalAddress[] = {
0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x47,0x65,0x74,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x49,0x50,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,
0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x2f,
0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,
0x6c,0x6f,0x70,0x65,0x3e,
};
static const unsigned char raw_addPortMapping[] = {
0x3c,
0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x41,0x64,0x64,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,0x75,0x72,0x6e,0x3a,0x73,0x63,
0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x3e,0x3c,0x4e,0x65,0x77,0x45,0x78,
0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x3c,0x4e,0x65,
0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,
0x3c,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x28,0x25,0x54,0x79,0x70,0x65,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x3c,0x4e,0x65,0x77,0x45,0x6e,0x61,0x62,0x6c,0x65,0x64,0x3e,0x31,
0x3c,0x2f,0x4e,0x65,0x77,0x45,0x6e,0x61,0x62,0x6c,0x65,0x64,0x3e,0x3c,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x43,0x6c,0x69,0x65,0x6e,0x74,0x3e,0x28,0x25,0x4c,0x6f,0x63,0x61,0x6c,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x25,0x29,
0x3c,0x2f,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x43,0x6c,0x69,0x65,0x6e,0x74,0x3e,0x3c,0x4e,0x65,0x77,0x4c,0x65,0x61,0x73,0x65,0x44,0x75,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3e,0x30,0x3c,0x2f,0x4e,0x65,0x77,0x4c,0x65,0x61,0x73,
0x65,0x44,0x75,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3e,0x3c,0x4e,0x65,0x77,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x3e,0x28,0x25,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,
0x6f,0x6e,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x3e,0x3c,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,
0x2f,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,0x2f,0x6d,0x3a,0x41,0x64,0x64,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,
0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x3e,
};
static const unsigned char raw_deletePortMapping[] = {
0x3c,
0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x44,0x65,0x6c,0x65,0x74,0x65,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,0x75,0x72,0x6e,
0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x3e,0x3c,0x4e,0x65,
0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,
0x3c,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x28,0x25,0x54,0x79,0x70,0x65,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x3c,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,
0x74,0x3e,0x3c,0x2f,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,0x2f,0x6d,0x3a,0x44,0x65,0x6c,0x65,0x74,0x65,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,
0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x3e,
};
#define DECLARE_DATA(name)\
static const QByteArray data_##name = QByteArray::fromRawData((const char*)raw_##name, sizeof(raw_##name));
DECLARE_DATA(discover)
DECLARE_DATA(ppp_discover)
DECLARE_DATA(igd_discover)
DECLARE_DATA(queryExternalAddress)
DECLARE_DATA(addPortMapping)
DECLARE_DATA(deletePortMapping)
QUpnpPortMapper::QUpnpPortMapper(QObject *parent)
: QObject(parent)
{
m_udpSocket = NULL;
m_timer = NULL;
m_networkAccessManager = NULL;
m_discoverOk = false;
m_lastAddPortMappingOk = false;
m_lastDeletePortMappingOk = false;
}
QUpnpPortMapper::~QUpnpPortMapper()
{
close();
}
QHostAddress QUpnpPortMapper::localAddress()
{
return m_localAddress;
}
bool QUpnpPortMapper::isDiscoverOk()
{
return m_discoverOk;
}
bool QUpnpPortMapper::open(const QHostAddress & localAddress)
{
if (!m_localAddress.isNull())
{
qWarning() << QString("QUpnpPortMapper has already open");
return false;
}
if (localAddress.isNull())
return false;
if (localAddress.protocol() == QAbstractSocket::IPv6Protocol)
{
qWarning() << QString("QUpnpPortMapper does not support IPv6");
return false;
}
m_localAddress = localAddress;
m_controlUrl = QUrl();
m_networkAccessManager = new QNetworkAccessManager(this);
m_discoverOk = false;
m_lastExternalAddress = QHostAddress();
m_lastAddPortMappingOk = false;
m_lastDeletePortMappingOk = false;
connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
return true;
}
bool QUpnpPortMapper::open(const QNetworkInterface & networkInterface)
{
foreach(QNetworkAddressEntry entry, networkInterface.addressEntries())
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
return open(entry.ip());
return false;
}
void QUpnpPortMapper::close()
{
if (m_localAddress.isNull())
return;
if (m_udpSocket)
{
delete m_udpSocket;
m_udpSocket = NULL;
}
if (m_timer)
{
delete m_timer;
m_timer = NULL;
}
if (m_networkAccessManager)
{
delete m_networkAccessManager;
m_networkAccessManager = NULL;
}
m_localAddress = QHostAddress();
m_controlUrl = QString();
m_lastExternalAddress = QHostAddress();
m_lastAddPortMappingOk = false;
m_lastDeletePortMappingOk = false;
}
bool QUpnpPortMapper::discover(int timeout, bool wait)
{
if (m_discoverOk)
return false;
if (!m_udpSocket)
{
m_udpSocket = new QUdpSocket(this);
m_udpSocket->bind(m_localAddress, 0);
connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(my_onUdpSocketReadyRead()));
}
if (!m_timer)
{
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(onDiscoverTimeout()));
}
m_udpSocket->writeDatagram(data_ppp_discover, QHostAddress("239.255.255.250"), 1900);
m_timer->setSingleShot(true);
m_timer->start(timeout);
if (wait)
{
QEventLoop eventLoop;
connect(this, SIGNAL(discoverFinished(bool)), &eventLoop, SLOT(quit()));
eventLoop.exec();
return m_discoverOk;
}
return true;
}
QHostAddress QUpnpPortMapper::queryExternalAddress(bool wait)
{
if (!m_discoverOk)
return QHostAddress();
QNetworkRequest request(m_controlUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress");
QNetworkReply * reply = m_networkAccessManager->post(request, data_queryExternalAddress);
reply->setProperty("reply-type", "queryExternalAddress");
if (wait)
{
QEventLoop eventLoop;
connect(this, SIGNAL(queryExternalAddressFinished(QHostAddress,bool,QString)), &eventLoop, SLOT(quit()));
eventLoop.exec();
return m_lastExternalAddress;
}
return QHostAddress();
}
bool QUpnpPortMapper::addPortMapping(QAbstractSocket::SocketType type, QHostAddress internalAddress, quint16 internalPort, quint16 externalPort, QString description, bool wait)
{
if (!m_discoverOk)
return false;
QByteArray typeText;
if (type == QAbstractSocket::TcpSocket)
typeText = "TCP";
else if (type == QAbstractSocket::UdpSocket)
typeText = "UDP";
else
return false;
QByteArray content = data_addPortMapping;
content = content.replace("(%ExternalPort%)", QByteArray::number(externalPort));
content = content.replace("(%InternalPort%)", QByteArray::number(internalPort));
content = content.replace("(%Type%)", typeText);
content = content.replace("(%LocalAddress%)", internalAddress.toString().toUtf8());
content = content.replace("(%Description%)", description.toUtf8());
QNetworkRequest request(m_controlUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping");
QNetworkReply * reply = m_networkAccessManager->post(request, content);
reply->setProperty("reply-type", "addPortMapping");
if (wait)
{
QEventLoop eventLoop;
connect(this, SIGNAL(addPortMappingFinished(bool, QString)), &eventLoop, SLOT(quit()));
eventLoop.exec();
return m_lastAddPortMappingOk;
}
return true;
}
bool QUpnpPortMapper::deletePortMapping(QAbstractSocket::SocketType type, quint16 externalPort, bool wait)
{
if (!m_discoverOk)
return false;
QByteArray typeText;
if (type == QAbstractSocket::TcpSocket)
typeText = "TCP";
else if (type == QAbstractSocket::UdpSocket)
typeText = "UDP";
else
return false;
QByteArray content = data_deletePortMapping;
content = content.replace("(%ExternalPort%)", QByteArray::number(externalPort));
content = content.replace("(%Type%)", typeText);
QNetworkRequest request(m_controlUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#DeletePortMapping");
QNetworkReply * reply = m_networkAccessManager->post(request, content);
reply->setProperty("reply-type", "deletePortMapping");
if (wait)
{
QEventLoop eventLoop;
connect(this, SIGNAL(deletePortMappingFinished(bool, QString)), &eventLoop, SLOT(quit()));
eventLoop.exec();
return m_lastDeletePortMappingOk;
}
return true;
}
void QUpnpPortMapper::onDiscoverTimeout()
{
if (m_timer)
{
m_timer->stop();
m_timer->deleteLater();
m_timer = NULL;
}
if (m_udpSocket)
{
delete m_udpSocket;
m_udpSocket = NULL;
}
emit discoverFinished(false);
}
void QUpnpPortMapper::onUdpSocketReadyRead()
{
if (!m_udpSocket)
return;
while (m_udpSocket->hasPendingDatagrams())
{
char buffer[3000];
QHostAddress hostAddress;
quint16 port = 0;
const int bufferSize = m_udpSocket->readDatagram(buffer, sizeof(buffer), &hostAddress, &port);
const QString package = QString::fromUtf8(QByteArray::fromRawData(buffer, bufferSize));
const QString tab = "Location:";
const int tabPos = package.indexOf(tab, 0, Qt::CaseInsensitive);//匹配
if(tabPos < 0)
continue;
int lineEndPos = package.indexOf('\n', tabPos);//匹配
if (lineEndPos < 0)
lineEndPos = package.size();
QString location = package.mid(tabPos + tab.length(), lineEndPos - tabPos - tab.length());//截取中间的字符
location = location.trimmed();//过滤两端空白字符
QNetworkReply * reply = m_networkAccessManager->get(QNetworkRequest(QUrl(location)));
reply->setProperty("reply-type", "discover");
m_udpSocket->deleteLater();
m_udpSocket = NULL;
break;
}
}
void QUpnpPortMapper::my_onUdpSocketReadyRead()
{
if (!m_udpSocket)
return;
while (m_udpSocket->hasPendingDatagrams())
{
char buffer[3000];
QHostAddress hostAddress;
quint16 port = 0;
const int bufferSize = m_udpSocket->readDatagram(buffer, sizeof(buffer), &hostAddress, &port);
const QString package = QString::fromUtf8(QByteArray::fromRawData(buffer, bufferSize));
printf("recv buf:%s\n",buffer);
const QString tab = "Location:";
const int tabPos = package.indexOf(tab, 0, Qt::CaseInsensitive);//匹配url开头
if(tabPos < 0)
continue;
const QString end_tab="\r\n";
int lineEndPos = package.indexOf(end_tab, tabPos,Qt::CaseInsensitive);//匹配url结尾
if (lineEndPos < 0)
lineEndPos = package.size();
QString location = package.mid(tabPos + tab.length(), lineEndPos - tabPos - tab.length());//截取中间的字符
location = location.trimmed();//过滤两端空白字符
printf("control_url:begin at %d end at %d!\n",tabPos+tab.length(),lineEndPos);
printf("url:%s\n",location.toStdString().data());
QNetworkReply * reply = m_networkAccessManager->get(QNetworkRequest(QUrl(location)));
reply->setProperty("reply-type", "discover");
m_udpSocket->deleteLater();
m_udpSocket = NULL;
break;
}
}
void QUpnpPortMapper::onReplyFinished(QNetworkReply * reply)
{
reply->deleteLater();
const QByteArray type = reply->property("reply-type").toByteArray();
if (type == "discover")
{
printf("recv a discover reply\n");
onReplyDiscover(reply);
}
else if (type == "queryExternalAddress")
{
printf("recv a queryExternalAddress reply\n");
onReplyQueryExternalAddress(reply);
}
else if (type == "addPortMapping")
{
printf("recv a addPortMapping reply\n");
onReplyAddPortMapping(reply);
}
else if (type == "deletePortMapping")
{
printf("recv a deletePortMapping reply\n");
onReplyDeletePortMapping(reply);
}
}
void QUpnpPortMapper::onReplyDiscover(QNetworkReply * reply)
{
QDomDocument xml;
QString errorMsg;
int errorLine = 0;
int errorColumn = 0;
if (m_timer)
{
m_timer->stop();
m_timer->deleteLater();
m_timer = NULL;
}
if (xml.setContent(reply, &errorMsg, &errorLine, &errorColumn))
{
printf("xml:%s\n",xml.toString().toStdString().data());
QString relativeControlUrl = recursiveFindControlUrl(xml.documentElement());
printf("relativeControlUrl:%s\n",relativeControlUrl.toStdString().data());
if (relativeControlUrl.length() > 0)
m_controlUrl = reply->url().resolved(QUrl(relativeControlUrl));
}
if (m_controlUrl.isValid())
{
printf("set m_discoverOk to true!\n");
m_discoverOk = true;
}
emit discoverFinished(m_controlUrl.isValid());
}
void QUpnpPortMapper::onReplyQueryExternalAddress(QNetworkReply * reply)
{
QString content = QString::fromUtf8(reply->readAll());
QString addressText = getXmlTabContent(content, "NewExternalIPAddress", Qt::CaseInsensitive);
if (!addressText.isNull())
{
m_lastExternalAddress = QHostAddress(addressText);
emit queryExternalAddressFinished(m_lastExternalAddress, true, QString());
}
else
{
QString errorString = getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive);
m_lastExternalAddress = QHostAddress();
emit queryExternalAddressFinished(QHostAddress(), false, errorString);
}
}
void QUpnpPortMapper::onReplyAddPortMapping(QNetworkReply * reply)
{
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString content = QString::fromUtf8(reply->readAll());
//printf("content:%s\n",content.toStdString().data());
if (statusCode == 200)
{
m_lastAddPortMappingOk = true;
emit addPortMappingFinished(true, QString());
}
else
{
m_lastAddPortMappingOk = false;
emit addPortMappingFinished(false, getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive));
}
}
void QUpnpPortMapper::onReplyDeletePortMapping(QNetworkReply * reply)
{
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString content = QString::fromUtf8(reply->readAll());
printf("content:%s\n",content.toStdString().data());
if (statusCode == 200)
{
m_lastDeletePortMappingOk = true;
emit deletePortMappingFinished(true, QString());
}
else
{
m_lastDeletePortMappingOk = false;
emit deletePortMappingFinished(false, getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive));
}
}
QString QUpnpPortMapper::recursiveFindControlUrl(const QDomElement & parent)
{
for (QDomElement device = parent.firstChildElement("device");
!device.isNull(); device = device.nextSiblingElement("device"))
{
QDomElement serviceList = device.firstChildElement("serviceList");
for (QDomElement service = serviceList.firstChildElement("service");
!service.isNull(); service = service.nextSiblingElement("service"))
{
const QString serviceType = service.firstChildElement("serviceType").text();
if (serviceType == "urn:schemas-upnp-org:service:WANPPPConnection:1")
return service.firstChildElement("controlURL").text();
}
QString result = recursiveFindControlUrl(device.firstChildElement("deviceList"));
if(result.length() > 0)
return result;
}
return QString();
}
QString QUpnpPortMapper::getXmlTabContent(QString content, QString tab, Qt::CaseSensitivity cs)
{
QRegExp rx(QString("<%1>([^\\r\\n]*)</%1>").arg(tab));
rx.setCaseSensitivity(cs);
if (content.indexOf(rx) >= 0)
return rx.capturedTexts().at(1);
else
return QString();
}
/*main.cpp*/
#include <QCoreApplication>
#include <QNetworkInterface>
#include "QUpnpPortMapper.h"
#include <iostream>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// list all NetworkInterface and choose one, or you can get your local address by calling a connected QTcpSocket with localAddress()
QUpnpPortMapper mapper;
QList<QNetworkInterface> allCards = QNetworkInterface::allInterfaces();
for (int i = 0; i < allCards.size(); ++i)
{
QString line = QString("[%1] %2").arg(i + 1).arg(allCards[i].humanReadableName());
qDebug() << line;
}
std::cout << "Choose : ";
int index = 0;
std::cin >> index;
if (index < 1 || index > allCards.size())
return 0;
// firstly, open
mapper.open(allCards.at(index - 1));
qDebug() << QString("Internal address : %1").arg(mapper.localAddress().toString());
// then discover
if (!mapper.discover(5000, true))
{
qDebug() << "No upnp support";
return 0;
}
// however, queryExternalAddress is not necessary
qDebug() << "External address " << mapper.queryExternalAddress(true).toString();
qDebug() << "Input internal & external port:";
int internalPort, externalPort;
std::cin >> internalPort ;//>> externalPort;
// you can use TCP or UDP, the description must be REGULAR in order to not corrupt the posted XML
bool ok = mapper.addPortMapping(QAbstractSocket::TcpSocket, mapper.localAddress(), internalPort, externalPort, "test", true);
if (ok)
qDebug() << "Mapping ok";
else
{
qDebug() << "Mapping failed";
return 0;
}
// bool ok2=mapper.deletePortMapping(QAbstractSocket::TcpSocket, internalPort,true);
// if (ok2)
// qDebug() << "delete "<<internalPort<<"Mapping ok";
// else
// {
// qDebug() << "delete "<<internalPort<<"Mapping failed";
// return 0;
// }
return app.exec();
}