📄 btjl.txt
字号:
QString get = QString("GET ") + post + " HTTP/1.1\r\nHOST: " + host + "\r\nACCEPT-LANGUAGE: en\r\n\r\n";
QProxy proxy; //因為UPnP設備基本上是局域網內部的操作,不需要代理,為了避免全局的代理設置影響後續TCP操作,這裡定義一個空的代理.
QSocket tcp;
int retry = 0;
if(delay<=0)delay = UPNPDELAY;
if(!tcp.Connect(addr.c_str(),port,false,&proxy))return false;//connection failed
tcp.SendData(get.c_str(),get.Length());
do
{
if(tcp.Write()>=0&&tcp.SendLength()>0) sleep(1); //sleep(x) = Sleep(x * 1000)
}while(++retry<delay&&tcp.SendLength()>0);
if(tcp.SendLength()>0||tcp.Read()<0)return false;
retry = 0;
do
{
if(retry)sleep(1);
}while(tcp.Read()>=0&&++retry<delay&&tcp.Read()>=0);
char a[50], b[50], c[50];
if(SplitterString(tcp.RecvBuffer(),a,b,c,40,40,40)!=3||b[0]!='2')return false;
const char* lfe = NULL;
const char* buf = tcp.RecvBuffer(); //如果當前沒有數據,返回"",RecvBuffer永遠不會返回NULL,所以不用擔心空指針
const char* lfr = NULL;
//get friendly name
lfr = strstr(buf,"<friendlyName>");
if(lfr)
{
lfr += (sizeof("<friendlyName>") - 1);
lfe = strstr(lfr,"</friendlyName>");
if(lfe)m_friendlyname = QString(lfr, lfe - lfr).Trim();
}
//get <modelName>
lfr = strstr(buf,"<modelName>");
if(lfr)
{
lfr += (sizeof("<modelName>") - 1);
lfe = strstr(lfr, "</modelName>");
if(lfe)m_modelname = QString(lfr, lfe - lfr).Trim();
}
//get <URLBase>
lfr = strstr(buf,"<URLBase>");
if(lfr)
{
lfr += (sizeof("<URLBase>") - 1);
lfe = strstr(lfr,"</URLBase>");
if(lfe)m_baseurl = QString(lfr, lfe - lfr).Trim();
}
if(m_baseurl.IsEmpty())m_baseurl = QString("http://") + host + "/";
if(m_baseurl[m_baseurl.Length()]!='/')m_baseurl += "/";
//get service
QString svrn = QString("<serviceType>") + m_name + "</serviceType>";
lfr = strstr(buf,svrn.c_str());
if(lfr)
{
lfe = strstr(lfr, "</service>");
if(lfe)
{
QString tmp = QString(lfr, lfe - lfr);
lfr = strstr(tmp.c_str(),"<controlURL>");
if(lfr)
{
lfr += (sizeof("<controlURL>") - 1);
lfe = strstr(lfr,"</controlURL>");
if(lfe&&lfe>lfr)
{
if(lfr[0]=='/')
{//relative url
lfr++;
m_controlurl = m_baseurl + QString(lfr, lfe - lfr);
}
else
{
m_controlurl = QString(lfr, lfe - lfr);
}
}
}
}
}
return Comfirmed();
}
/*GetProperty: 在對像已經有效的情況下獲取對象的某屬性.
實現過程: 通過對目標對象的控制URL發送UPnP規範定義的POST請求並解析返回數據來獲取相應的屬性.
參數:
name: 針對該屬性的動作,如:GetExternalIPAddress
rsp: 屬性返回值的名稱,如:NewExternalIPAddress
delay: 最大等待時間*/
QString QMyUPNP::GetProperty(const QString& name, const QString& rsp, int delay)
{
if(!Comfirmed())return QString();
QString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_controlurl, post, host, port);
if(addr.IsEmpty()||!is_validport(port))return QString();
QMemoryStream cnt; //QMemoryStream 內部有智能的內存算法可以避免頻繁的內存分配.
QMemoryStream psr;
cnt.Write("<s:Envelope\r\n xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n ");
cnt.Write("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <s:Body>\r\n <u:");
cnt.Write(name.c_str());
cnt.Write(" xmlns:u=\"");
cnt.Write(m_name.c_str());
cnt.Write("\">\r\n </u:");
cnt.Write(name.c_str());
cnt.Write(">\r\n </s:Body>\r\n</s:Envelope>\r\n\r\n");
psr.Write("POST ");
psr.Write(post.c_str());
psr.Write(" HTTP/1.1\r\nHOST: ");
psr.Write(host.c_str());
psr.Write("\r\nContent-Length: ");
psr.Write(cnt.Size());
psr.Write("\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPACTION: ");
psr.Write(m_name.c_str());
psr.Write("#");
psr.Write(name.c_str());
psr.Write("\r\n\r\n");
psr.Write(cnt.Data(),cnt.Size());
QProxy proxy;
QSocket tcp;
int retry = 0;
if(delay<=0)delay = UPNPDELAY;
if(!tcp.Connect(addr.c_str(),port,false,&proxy))
{
return QString();//no proxy
}
tcp.SendData(psr.Data(),psr.Size());
do
{
if(tcp.Write()>=0&&tcp.SendLength()>0) sleep(1);
}while(++retry<delay&&tcp.SendLength()>0);
if(tcp.SendLength()>0||tcp.Read()<0)
{
return QString();
}
retry = 0;
do
{
if(retry)sleep(1);
}while(tcp.Read()>=0&&++retry<delay&&tcp.Read()>=0);
char a[50], b[50], c[50];
if(SplitterString(tcp.RecvBuffer(),a,b,c,40,40,40)!=3||b[0]!='2')
{
return QString();
}
QString schr = QString("<") + rsp;
QString echr = QString("</") + rsp + ">";
const char* lfe = NULL;
const char* buf = tcp.RecvBuffer();
const char* lfr = strstr(buf,schr.c_str());
if(lfr)
{
lfe = strstr(lfr, echr.c_str());
if(lfe)
{
QString ret = QString(lfr, lfe - lfr);
lfr = strchr(ret.c_str(),'>');
if(lfr)
{
lfr++;
return QString(lfr);
}
}
}
return QString();
}
/*GetArgString: 將BS使用的一個QStringList的參數格式轉換成符合UPnP規範的字串型參數.*/
QString GetArgString(QStringList& args)
{
QMemoryStream ms;
for(int i=0;i<args.Count()-1;i+=2)
{
ms.Write(" <",5);
ms.Write(args.Item(i).c_str());
ms.Write(">",1);
ms.Write(args.Item(i+1).c_str());
ms.Write("</",2);
ms.Write(args.Item(i).c_str());
ms.Write(">\r\n",3);
}
return QString(ms.Data(),ms.Size());
}
/*InvokeCommand: 在對像有效的情況下調用對象的方法.
實現過程:通過向目標對象的控制URL發送UPnP規範定義的POST方法來調用對象的方法.
參數含義:
name: 調用方法的名稱.如:AddPortMapping
args: QStringList類型,其組成結構為:偶數索引的值為參數名,奇數索引的值為參數值,從0開始索引.
delay: 最大等待時間.*/
bool QMyUPNP::InvokeCommand(const QString& name, QStringList& args, int delay)
{
if(!Comfirmed())return false;
QString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_controlurl, post, host, port);
if(addr.IsEmpty()||!is_validport(port))return false;
QMemoryStream cnt;
QMemoryStream psr;
cnt.Write("<s:Envelope\r\n xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n ");
cnt.Write("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <s:Body>\r\n <u:");
cnt.Write(name.c_str());
cnt.Write(" xmlns:u=\"");
cnt.Write(m_name.c_str());
cnt.Write("\">\r\n");
cnt.Write(GetArgString(args).c_str());
cnt.Write(" </u:");
cnt.Write(name.c_str());
cnt.Write(">\r\n </s:Body>\r\n</s:Envelope>\r\n\r\n");
psr.Write("POST ");
psr.Write(post.c_str());
psr.Write(" HTTP/1.1\r\nHOST: ");
psr.Write(host.c_str());
psr.Write("\r\nContent-Length: ");
psr.Write(cnt.Size());
psr.Write("\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPACTION: ");
psr.Write(m_name.c_str());
psr.Write("#");
psr.Write(name.c_str());
psr.Write("\r\n\r\n");
psr.Write(cnt.Data(),cnt.Size());
QProxy proxy;
QSocket tcp;
int retry = 0;
if(delay<=0)delay = UPNPDELAY;
if(!tcp.Connect(addr.c_str(),port,false,&proxy))return false;//no proxy
tcp.SendData(psr.Data(),psr.Size());
do
{
if(tcp.Write()>=0&&tcp.SendLength()>0) sleep(1);
}while(++retry<delay&&tcp.SendLength()>0);
if(tcp.SendLength()>0||tcp.Read()<0)return false;
retry = 0;
do
{
if(retry)sleep(1);
}while(tcp.Read()>=0&&++retry<delay&&tcp.Read()>=0);
char a[50], b[50], c[50];
if(SplitterString(tcp.RecvBuffer(),a,b,c,40,40,40)==3&&b[0]=='2')return true;
return false;
}
/*GetAddPortmapArgs: 生成相應可用於InvokeCommand的針對AddPortmapping的參數.*/
void GetAddPortmapArgs(QStringList& args, int eport, int iport,
const char* iclient, const char* descri,
bool enabled, int dur, const char* type, const char* remote)
{
args.Clear();
args.Add("NewRemoteHost");
args.Add(remote);
args.Add("NewExternalPort");
args.Add(QString(eport));
args.Add("NewProtocol");
args.Add(type);
args.Add("NewInternalPort");
args.Add(QString(iport));
args.Add("NewInternalClient");
args.Add(iclient);
args.Add("NewEnabled");
args.Add(enabled?"1":"0");
args.Add("NewPortMappingDescription");
args.Add(descri);
args.Add("NewLeaseDuration");
args.Add(QString(dur));
}
void GetDeletePortmapArgs(QStringList& args, int eport, const char* type, const char* remote)
{
args.Clear();
args.Add("NewRemoteHost");
args.Add(remote);
args.Add("NewExternalPort");
args.Add(QString(eport));
args.Add("NewProtocol");
args.Add(type);
}
/*附註:
1, 以上用到的QString,QUdpSocket,QStringList,QMemoryStream,QSocket,QProxy都是BS內部實現的類,功能與接口與一些流行的類庫如MFC,VCL類似.
2, 通過QMyUPNP類來查找支持自動端口映射的設備的示例代碼:
QMyUPNP Tmp;
//查找服務的示例
if(!Tmp.Comfirmed()) Tmp.Search(UPNPPORTMAP1);
if(!Tmp.Comfirmed()) Tmp.Search(UPNPPORTMAP0);
if(Tmp.Comfirmed()) MessageBox(NULL,(QString("Found: ") + Tmp.GetModelName()).c_str(),"",MB_OK);
//添加端口的示例
QStringList args;
GetAddPortmapArgs(args,FWANPort,FLocalPort,FLocalIP,FDescription,true,0,FType,FWANIP);//FWANIP為""表示默認公網IP
Tmp.InvokeCommand(UPNPADDPORTMAP,args,6);
//刪除端口的示例
QStringList args;
GetDeletePortmapArgs(args,FWANPort,FType,FWANIP);
Tmp.InvokeCommand(UPNPDELPORTMAP,args,6);
//獲取公網IP的示例
QString ip = Tmp.GetProperty(UPNPGETEXTERNALIP,6);
MessageBox(NULL,(QString("Your WAN IP: ") + ip).c_str(),"",MB_OK);*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -