ssdp协议近似于http协议,事实上,和http协议相似得地方就是他得协议内容,当然,我们要去除他得端口和d类地址。
为什么我在给其他员工或者面试得时候要他人深入一些,理解一下http协议,是因为理解了http协议,掌握ssdp也就不远了,很多人可能会问:http协议有啥内容,无非就是get,post,put,delete 么,还能怎么样,我经常问他们一点:http协议怎么知道他结束了?
大部分面试者支支吾吾答不出来,就这么奇怪,有一部分人说socket.close(), socket 关闭是因为你知道结束了才关闭,不是因为关闭知道http协议结束。两个\r\n\r\n代表http协议内容部分结束,至于二进制,当然有content-length 字段去表述了。我们来看一下ssdp协议:
static const char* ssdp_search =
"M-SEARCH * HTTP/1.1"
"HOST: 239.255.255.250:1900"
"MAN: \"ssdp:discover\""
"MX: 5"
"ST: ssdp:all";
这代表了搜索所有设备,这样对否,能出结果否,在239.255.255.250 这种d类ip地址上,端口1900发出该字符串,应该收到很多设备发出得信息,例如摄像头信息,你一定会搜到,不过,这一段代码搜索不到?为什么?看正确得写法:
static const char* ssdp_search =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: 5\r\n"
"ST: ssdp:all\r\n\r\n";
虽然ssdp是udp协议,但是他依然需要\r\n来代表行结束,\r\n\r\n代表协议内容部分结束。这样,就会搜索到所有信息,当然了,我们可以使用过滤,只搜索部分协议,比如就是摄像头,其他设备忽略。
2、发现谁在发现除了搜索设备,我们还需要知道谁往我们得服务地址发送了搜索地址得需求,因为我们是一个设备,其他在gb28181 服务中,我们需要知道sip 网守和网关得设备,可能有多个这种设备,我们则需要知道谁正需要发现设备,我们写出以下代码:
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
int main_2()
{
int iRet = 0;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr;
addr.sin_family = AF_INET;
//addr.sin_addr.S_un.S_addr = INADDR_ANY;
addr.sin_addr.S_un.S_addr = inet_addr("192.168.0.129");
addr.sin_port = htons(1900);
bool bOptval = true;
iRet = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptval, sizeof(bOptval));
if (iRet != 0) {
printf("setsockopt fail:%d", WSAGetLastError());
return -1;
}
iRet = ::bind(sock, (sockaddr*)&addr, sizeof(addr));
if (iRet != 0) {
printf("bind fail:%d", WSAGetLastError());
return -1;
}
printf("socket:%d bind success\n", sock);
// 加入组播
ip_mreq multiCast;
//multiCast.imr_interface.S_un.S_addr = INADDR_ANY;
multiCast.imr_interface.S_un.S_addr = inet_addr("192.168.0.129");
multiCast.imr_multiaddr.S_un.S_addr = inet_addr("239.255.255.250");
iRet = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multiCast, sizeof(multiCast));
if (iRet != 0) {
printf("setsockopt fail:%d", WSAGetLastError());
return -1;
}
printf("udp group start\n");
int len = sizeof(sockaddr);
char strRecv[1500] = { 0 };
while (true)
{
memset(strRecv, 0, sizeof(strRecv));
iRet = recvfrom(sock, strRecv, sizeof(strRecv) - 1, 0, (sockaddr*)&addr, &len);
if (iRet
1
0
urn:schemas-upnp-org:device:DigitalSecurityCamera:1
HIKVISION iDS-ECD8012-M/E - F84224570
HIKVISION
http://www.hikvision.com
IP Camera
HIKVISION iDS-ECD8012-M/E
iDS-ECD8012-M/E
http://www.hikvision.com
F84224570
uuid:Upnp-iDS-ECD8012-M/E-1_0-F84224570
urn:schemas-upnp-org:service:EmbeddedNetDeviceControl:1
urn:upnp-org:serviceId:EmbeddedNetDeviceControl
/
/
/
http://192.168.0.64:80
从中可以发现很多信息 那么现在我们得GB28181 服务有sip 服务,网关服务,中心节点服务,存储服务,推理服务,我们就必须标明自己得设备,并且写好XML文件,让对方获取,我们当然不必墨守成规,可以改成其他形式得文件,不过要考虑兼容性。
封装测试在ssdp协议封装过程中,最为重要得一定是这个注意点,就是本机IP和主播地址IP,我们必须设置两个地址,在windows里面和linux下表现不同,必须要注意
int main(int argc, char* argv[])
{
asio::io_context io_service;
receiver r(io_service,
asio::ip::address::from_string("192.168.0.129"),
asio::ip::address::from_string("239.255.255.250"));
thread t([&r,&io_service] {
r.do_init("192.168.0.129");
r.StartTimer();
io_service.run();
});
while (1)
{
Sleep(3000);
}
return 0;
}