关于socket组播和ssdp
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于socket组播和ssdp相关的知识,希望对你有一定的参考价值。
1、说明
在制作的过程中,实际上ssdp发现协议特别简单,只是加入组播后,发送搜索的字符串,然后再在单播上接收,如果是发送,则要发送到多播地址,而且,发送的字符串不能出错,这里说明作者的一个错误,开始时,“MAN: “ssdp:discover”\\r\\n”,一直写成了"MAN: ssdp:discover\\r\\n",所以在单播上没有收到数据,值得注意!
2、show me the code,以下用boost库来做组播的接收和发送
socket.bind(
udp::endpoint(boost::asio::ip::address_v4::any(),
receiver ? port /* same as multicast port /
: 6200 / any */));
以上这句话比较重要,在使用发送的时候,使用组播端口,接收的时候,使用本地的一个端口,切记!
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
// 3rd party includes.
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <string>
static const char msearchmsgfmt[] = "M-SEARCH * HTTP/1.1\\r\\n"
"Connection: close"
"HOST: 239.255.255.250:1900\\r\\n"
"ST: %s\\r\\n"
"MAN: \\"ssdp:discover\\"\\r\\n"
"USER-AGENT: qbupnp 1.0"
"MX: 3\\r\\n\\r\\n";
void read(boost::asio::ip::udp::socket& socket)
{
boost::asio::ip::udp::endpoint sender;
std::vector<char> buffer;
std::size_t bytes_readable = 0;
for (;;)
{
// Poll until data is available.
while (!bytes_readable)
{
// Issue command to socket to get number of bytes readable.
boost::asio::socket_base::bytes_readable num_of_bytes_readable(true);
socket.io_control(num_of_bytes_readable);
// Get the value from the command.
bytes_readable = num_of_bytes_readable.get();
// If there is no data available, then sleep.
if (!bytes_readable)
{
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
}
// Resize the buffer to store all available data.
buffer.resize(bytes_readable);
// Read available data.
socket.receive_from(
boost::asio::buffer(buffer, bytes_readable),
sender);
// Extract data from the buffer.
std::string message(buffer.begin(), buffer.end());
// Output data.
std::cout << "Received message: ";
std::cout << message << std::endl;
}
}
void write(boost::asio::ip::udp::socket& socket,
boost::asio::ip::udp::endpoint& destination)
{
std::string message;
char buffer[256];
sprintf(buffer, msearchmsgfmt, "upnp:rootdevice");
/*socket.async_send_to(
boost::asio::buffer(buffer, strlen(buffer)), endpoint_,
boost::bind(handle_send_to, this,
boost::asio::placeholders::error));*/
for (unsigned int i = 0; i < 3; ++i)
{
//std::ostringstream stream;
//stream << i;
//message = stream.str();
socket.send_to(boost::asio::buffer(msearchmsgfmt, strlen(msearchmsgfmt)), destination);
std::cout << "Sent message: " << message << std::endl;
}
}
int main(int argc, char* argv[])
{
// Extract command-line arguments.
bool receiver = false;// std::string(argv[1]) == "receive";
boost::asio::ip::address address =
boost::asio::ip::address::from_string("239.255.255.250");
unsigned short port = 1900;
// Create socket.
using boost::asio::ip::udp;
boost::asio::io_service service;
udp::socket socket(service);
socket.open(boost::asio::ip::udp::v4());
// Allow other processes to reuse the address, permitting other processes on
// the same machine to use the multicast address.
socket.set_option(udp::socket::reuse_address(true));
socket.set_option(boost::asio::ip::multicast::enable_loopback(true));
socket.bind(
udp::endpoint(boost::asio::ip::address_v4::any(),
receiver ? port /* same as multicast port */
: 6200 /* any */));
udp::endpoint destination(address, port);
// Join group.
namespace ip = boost::asio::ip;
socket.set_option(ip::multicast::join_group(address));
// Start read or write loops based on command line options.
if (receiver)
read(socket);
else
write(socket, destination);
return 0;
}
3、有关于wireshark 抓包
注意使用port 1900 的包在局域网里面应该是有很多的,包含网关的,chrome 发送的ssdp,等等,读者可以适当自己过滤加上not host 192.168.0.1 诸如此类的网关过滤
4 、 接下来使用http去接收和发送数据
建议
1 使用boost asio库直接接收发送,甚至直接使用asio
2 使用httplib接收发送
3 使用nlohmann 解析json
4 xml 库可以使用ixml,或者直接手动解析:
如何手动解析xml
以上为什么推荐使用httplib,和 nlohmann,这两个只要加入头文件即可,不用编译。为什么使用asio,可以直接使用头文件,简化加载库,以及asio封装得非常方便。
5、总结
使用以上工具和方法,作者做出了一个ssdp搜索和控制工具,仿照libpnp来做的,研读了很多代码,读者可以沿着作者走过的路走一遍。
打印控制点
投屏服务客户端
总之,dlna和libupnp协议并不难懂,其实就是一系列协议的组合,甚至大量使用了http协议,在文件点播和直播方面,可以使用rtsp协议和http协议等等,只要深度研究这些基础协议,就可以做出相应的产品。
以上都是作者的事件经验,读者需要交流,可以使用418511899@qq.com,或者微信联系。
以上是关于关于socket组播和ssdp的主要内容,如果未能解决你的问题,请参考以下文章