上一章我们解析完了upnp协议中的http头部获取了xml文件的地址,使用http协议我们再次获取xml文件后需要解析xml,我们最害怕的就是引入一个又一个库,事实上,libupnp就引入了很多库,造成了程序臃肿,打印频繁,我们使用c语言的strstr和sscanf来手动解析xml,获取特定字符串。
1、分解urlupnp协议中的LOCATION是从http头部获取而来,要知道如何获取,请看这一片文章
解析http头部 接下来就是使用sscanf函数来获取http 的host 端口和路由信息,如 http://192.168.1.144:1551/description.xml 我们要分解成为 http://192.168.1.144:1551/ 和路由 /description,使用一个非常绚丽的技巧,如下所示:
const char *pos = ss["LOCATION"].c_str();
//const char *url = "http://192.168.1.144:1551/description.xml";
char http[256] = {"http://" };
char route[256] = { 0 };
if (*(pos + 4) == ':')
pos += 7;
sscanf(pos, "%[^/]%s", &http[7],route);
以上先把http://填入缓冲中,然后让sscanf 中缓冲的指针指向第7个字节,开始往里放解析值,sscanf函数跳过http://,找到我们字符串的下一个"/"位置。 ok,我们使用http协议下载xml文件,下载结束,得到xml body
1、下载下来后xml body我们使用http协议在程序中拿到了xml body体
1
0
uuid:bb5e-21ce-1111-11b2-f918-ec9c-3235-709a-
Living Room 1_5336_HiDMR
urn:schemas-upnp-org:device:MediaRenderer:1
Hisilicon Technologies Co., Ltd
http://www.Hisilicon.com
Hisilicon MediaRenderer
1.1
http://www.Hisilicon.com
DMR-1.50
urn:schemas-upnp-org:service:ConnectionManager:1
urn:upnp-org:serviceId:ConnectionManager
//需要获得下面这个controlURL
/upnp/service/ConnectionManager/Control
/upnp/service/ConnectionManager/Event
/upnp/service/cms.xml
urn:schemas-upnp-org:service:AVTransport:1
urn:upnp-org:serviceId:AVTransport
//需要获得下面这个controlURL
/upnp/service/AVTransport/Control
/upnp/service/AVTransport/Event
/upnp/service/avt.xml
urn:schemas-upnp-org:service:RenderingControl:1
urn:upnp-org:serviceId:RenderingControl
//需要获得下面这个controlURL
/upnp/service/RenderingControl/Control
/upnp/service/RenderingControl/Event
/upnp/service/rcs.xml< / SCPDURL>
这是一份upnp协议中的设备xml 描述,里面包含了几个关键信息,需要我们获取 我们需要的是:
1 设备名称 2 AVTransport 传输点的控制url 3 RenderingControl 点的控制url
熟悉upnp协议的同志们知道这个非常关键,下面我们来制定解决方案
2、定义数据结构typedef struct s_xmlanalyse_c
{
char friendlyName[64];
char url_AVTransport[256];
char url_RenderingControl[256];
char url_ConnectionManager[256];
}s_xmlanalyse_c;
定义得到单个节点的内容函数,使用strstr函数来得到xml 字符串中的位置,将字符串内容使用memcpy来复制,字符串的本质是可打印并且最后一个字符是’\0’,因此我们使用memcpy 和 '\0’来生成新的字符串。
const char *GetValueOf_c(const char *sbody, const char *name, char *ret,int retlen)
{
if (strlen(name) > 63)
return NULL;
char buf_fn1[64];
char buf_fn2[64];
sprintf(buf_fn1, "", name);
sprintf(buf_fn2, "", name);
const char *start = sbody;
const char *end = sbody + strlen(sbody);
char *fn = &buf_fn1[0];
char *fnn = &buf_fn2[0];
int len = strlen(fn);
const char * x1 = strstr(start, fn);
const char * x2 = strstr(start + len,fnn);
if (x1 != NULL && x2 != NULL)
{
x1 += len;
int len2 = (int)(x2 - x1);
if (len2 > retlen - 1)
return NULL;
memcpy(ret, x1, x2 - x1);
ret[x2 - x1] = '\0';
return x2 + len+1;
}
return NULL;
}
获取三个节点内容
使用三个循环获取内容
int GetValueOf_Service_c(const char *fpos, s_xmlanalyse_c *value)
{
const char * fname = "serviceType";
const char * fname2 = "controlURL";
int flag = 0;
char tmp[256];
for (int i = 0; i url_RenderingControl,sizeof(value->url_RenderingControl));
flag++;
}
}
if (flag == 3)
return 0;
return -1;
}
总结
我们使用分解分而治之对待软件工程的方式,首先 A 定义数据结构 B 得到流程 --》2.1 http 头部分解–》2.2 分解url --》2.3获取xml --》2.4获取节点内容 C 定义截取url函数 其中2.4 分解为如下步骤 D 定义获取节点内容的函数 E 定义 调用4 函数得到多个节点内容的函数