关于socket组播和ssdp

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于socket组播和ssdp相关的知识,希望对你有一定的参考价值。

关于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来做的,研读了很多代码,读者可以沿着作者走过的路走一遍。
使用以上工具制作的仿libupnp
打印控制点
控制点描述
投屏服务客户端
投屏服务客户端

总之,dlna和libupnp协议并不难懂,其实就是一系列协议的组合,甚至大量使用了http协议,在文件点播和直播方面,可以使用rtsp协议和http协议等等,只要深度研究这些基础协议,就可以做出相应的产品。

以上都是作者的事件经验,读者需要交流,可以使用418511899@qq.com,或者微信联系。

以上是关于关于socket组播和ssdp的主要内容,如果未能解决你的问题,请参考以下文章

asio 组播包ssdp

ssdp安全-攻击和防御

图说单播,组播,广播,选播和地域播

Rxjs: 单播和多播

组播和广播的概念,IGMP的用途

vxlan vs GRE(三层组播和二层组播如何对应起来)