设备发现协议SSDP实现

本文探讨了使用UDP进行SSDP(Simple Service Discovery Protocol)时,服务端如何正确设置socket加入239.255.255.250:1900组播,并修复了发送组播消息到239.255.255.250的问题。客户端和服务器端的代码示例及常见错误分析提供了深入理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原理:

1.将socket加入239.255.255.250,端口 1900

2.客户端:通过设置setsockopt +IPPROTO_IP,IP_ADD_MEMBERSHIP属性,可向ssdp组进行组播。

3.服务端:通过设置绑定239.255.255.250:1900进行数据接收,通过setsockopt +IPPROTO_IP,IP_ADD_MEMBERSHIP属性 加入组播。

容易错误的地方:

服务端打印sendto成功,但是通过wireshark抓包发现没有组播发送到239.255.255.250

这是因为:sendto的时候,用的sockaddr_in是通过recvfrom得到的:recvfrom得到的是具体的某个从组播拷贝的数据的源ip,这是单播。如果要组播发送,则要发送到这个地址:
    m_cddr.sin_family = AF_INET;
    m_cddr.sin_addr.s_addr = inet_addr("239.255.255.250");
    m_cddr.sin_port = htons(1900);

客户端代码:

/*
Author:zhuoyong
Date:2022-04-07
Mark:人脸识别客户端代码
*/

#ifndef CSSDPCLIENT_H
#define CSSDPCLIENT_H
#include<string>
#include<string.h>
#include<queue>
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<thread>
#include<chrono>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<net/if.h>
#include<pthread.h>
#include<fcntl.h>
#define ERR printf
#define RTM printf
#define MAXSSDPSIZE 1024

using namespace std;

class CSSDPClient
{
public:
    static  void    Discover();
    //最终加密后的pssword=:md5(<uuid>:<uid>:<pwd>)
    static  void    ModifyNet(string &strUuid,string&strUid,string&strPwd,string &strMask,string&strGateway,string &strDns);
    //strCryptPwd=base64(AES(pwd))
    static  void    ModifyUser(string &strUuid,string&strUid,string&strPwd);
protected:
    CSSDPClient();
    ~CSSDPClient();
    void    SendMsg(const string&str);
    void    RecvMsg();
    void    NotifyOne(const string&str);
    string  MakeSSDHeader();
    string  MakeSearchMsg();
    string  MakeModifyNetMsg();
    string  MakeModifyUserMsg();
    static  CSSDPClient *Ins();
    void    InerDiscover(const string&str);
    void    InitSocket();
private:
    static CSSDPClient *    g_in;
    sockaddr_in             m_sddr ;
    int                     m_ssdps;
    queue<string>           m_que;
    pthread_cond_t          m_cd;
    pthread_mutex_t         m_mt;
};

#endif // CSSDPCLIENT_H
/*
 * ===========================================================================
 *
 *       Filename:  SSDPClient.cpp
 *    Description:  客户端设备发现 xml协议
 *        Version:  1.0
 *        Created:  20220406-12:07
 *       Revision:  none
 *       Compiler:  g++
 *         Author:   (zhouyongku),
 *        Company:  
 *
 * ===========================================================================
 */

#include"SSDPClient.h"



CSSDPClient *CSSDPClient::g_in=NULL;
//全局变量


//解析IP地址
CSSDPClient::CSSDPClient()
{
    m_mt=PTHREAD_MUTEX_INITIALIZER;
    m_cd=PTHREAD_COND_INITIALIZER;

    std::thread t([&]
    {
        InitSocket();
        while(true)
        {

            pthread_mutex_lock(&m_mt); // 拿到互斥锁,进入临界区
            if( m_que.size()<=0 )pthread_cond_wait(&m_cd,&m_mt); // 令线程等待在条件变量上
            pthread_mutex_unlock(&m_mt); // 释放互斥锁

            InerDiscover(m_que.front());

            m_que.pop();

        }


    });
    t.detach();
}

CSSDPClient::~CSSDPClient()
{

}

CSSDPClient *CSSDPClient::Ins()
{
    if( CSSDPClient::g_in == nullptr )
    {
        CSSDPClient::g_in = new CSSDPClient();
    }

    return CSSDPClient::g_in;
}

void CSSDPClient::InerDiscover(const string&str)
{
    SendMsg( str );
    RecvMsg( );
}

void CSSDPClient::InitSocket()
{
    struct timeval TimeOut;
    TimeOut.tv_sec = 1;
    TimeOut.tv_usec = 0;


    m_ssdps=socket(AF_INET,SOCK_DGRAM,0);
    if( m_ssdps < 0)
    {
        ERR("CSSDPClient::InitSocket failed of create socket 239.255.255.250:1900");
        return;
    }

    bzero(&m_sddr, sizeof(m_sddr));
    m_sddr.sin_family = AF_INET;
    m_sddr.sin_addr.s_addr = inet_addr("239.255.255.250");
    m_sddr.sin_port = htons(1900);
    setsockopt(m_ssdps, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
    ip_mreq mreq;
    bzero(&mreq,sizeof(mreq));
    mreq.imr_multiaddr.s_addr =inet_addr("239.255.255.250");
    mreq.imr_interface.s_addr =htonl(INADDR_ANY);
    if( setsockopt(m_ssdps, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) <0 )
    {
        ERR("CSSDPServer::InitSocket failed of setsockopt IP_ADD_MEMBERSHIP");
        return;
    }

//    if( bind(m_ssdps, (struct sockaddr *)&m_sddr, sizeof(m_sddr)) < 0 )
//    {
//        ERR("CSSDPClient::InitSocket failed of bind socket %s:%d",SSDP_MCAST_ADDR,SSDP_PORT);
//    }
}

void CSSDPClient::Discover()
{
    string str=CSSDPClient::Ins()->MakeSearchMsg();
    CSSDPClient::Ins()->NotifyOne(str);

}

void CSSDPClient::ModifyNet(string &strUuid, string &strUid, string &strPwd, string &strMask, string &strGateway, string &strDns)
{
    string str=CSSDPClient::Ins()->MakeModifyNetMsg();
    CSSDPClient::Ins()->NotifyOne(str);
}

void CSSDPClient::ModifyUser(string &strUuid, string &strUid, string &strPwd)
{
    string str=CSSDPClient::Ins()->MakeModifyUserMsg();
    CSSDPClient::Ins()->NotifyOne(str);
}

void CSSDPClient::NotifyOne(const string&str)
{
    RTM("CSSDPClient::NotifyOne quesize=%d",m_que.size());
    pthread_mutex_lock(&m_mt); // 拿到互斥锁,进入临界区
    m_que.push( str );
    pthread_cond_signal(&m_cd); // 通知等待在条件变量上的消费者
    pthread_mutex_unlock(&m_mt); // 释放互斥锁
}

string CSSDPClient::MakeSSDHeader()
{
    string strMsg="M-SEARCH * HTTP/1.1\n";
    strMsg +="HOST: 239.255.255.250:1900\n";
    strMsg +="MAN: \"ssdp:discover\"\n";
    strMsg +="MX: 1\n";
    strMsg +="ST: urn:dial-multiscreen-org:service:dial:1\n";
    strMsg +="USER-AGENT:arm\n";
    return strMsg;
}

string CSSDPClient::MakeSearchMsg()
{

    string strMsg=MakeSSDHeader();
    strMsg +="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
    strMsg +="<Probe>\n";
        strMsg +="  <Uuid>";
        strMsg += "uuid-test";
        strMsg +="</Uuid>\n";
        strMsg +="  <Types>inquiry</Types>\n";
        strMsg +="  <DeviceType>";
        strMsg += "wodepingban";
        strMsg +="</DeviceType>\n";
    strMsg +="</Probe>";
    return strMsg;
}

string CSSDPClient::MakeModifyNetMsg()
{
    string strMsg=MakeSSDHeader();
    char szMsg[MAXSSDPSIZE]={0};
    sprintf( szMsg,
    "%s\n"
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
    "<Probe>\n"
    "<Uuid>testuuid</Uuid>\n"
    "<Types>update</Types>\n"
    "<DeviceSN>123456sdflsdfjl1023-1asdfgh</DeviceSN>\n"
    "<method>modifyipdns</method>\n"
    "<MAC>ab-cd-ef-gh-kh</MAC>\n"
    "<IPv4Address>192.168.66.24</IPv4Address>\n"
    "<IPv4SubnetMask>255.255.255.0</IPv4SubnetMask>\n"
    "<IPv4Gateway>192.168.66.254</IPv4Gateway>\n"
    "<IPv6Address>::</IPv6Address>\n"
    "<IPv6Gateway>::</IPv6Gateway>\n"
    "<IPv6MaskLen>64</IPv6MaskLen>\n"
    "<DHCP>false</DHCP>\n"
    "<Password>kFnsMaQrzmGi89g+6txepC1RNnZMSi/fA16x+UdjFOmqBmoVCc/zeZ8X6oZmLBdWaXnvwTxjLIQBsLsDP0xjHw==</Password>\n"
    "</Probe>\n",
    strMsg.c_str());
    return string(szMsg);

}

string CSSDPClient::MakeModifyUserMsg()
{
    string strMsg=MakeSSDHeader();


    char szMsg[MAXSSDPSIZE]={0};
    sprintf( szMsg,
    "%s\n"
     "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
     "<Probe>\n"
     "<Uuid>your uuid>\n"
     "<Types>update</Types>\n"
     "<DeviceSN>your device sn</DeviceSN>\n"
     "<method>modifypassword</method>\n"
     "<newpassword>your new md5 password</newpassword>\n"
     "<Password>your md5 password</Password>\n"
     "</Probe>\n");
    return string(szMsg);
}


//发送ssdp:discover消息
void CSSDPClient::SendMsg(const string&str)
{
    RTM("CSSDPClient::SendMsg msg=%s",str.c_str());

    socklen_t len = sizeof(m_sddr);
    int nRet=sendto(m_ssdps,str.c_str(),str.length(),0,(sockaddr*)&m_sddr,len);
    if( nRet >0 )
    {
        RTM("CSSDPClient::SendMsg leng=%d",nRet);

    }

    char szMsg[MAXSSDPSIZE]={0};
    nRet = recvfrom( m_ssdps,szMsg,MAXSSDPSIZE,0,(sockaddr*)&m_sddr,&len);
    if( nRet >0 )
    {
        RTM("CSSDPClient::SendMsg RECV MSG=%s",szMsg);
    }
}

void CSSDPClient::RecvMsg()
{
    socklen_t len = sizeof(m_sddr);
    char szMsg[MAXSSDPSIZE]={0};
    int num = recvfrom(m_ssdps,szMsg,MAXSSDPSIZE,0,(sockaddr*)&m_sddr,&len);
    if( num >0 )
    {
        string str;
        //ParseMsg(buf);
    }
}





服务端:

#ifndef CSSDPSERVER_H
#define CSSDPSERVER_H
/*
Author:zhuoyong
Date:2022-04-07
Mark:设备发现服务端代码
*/
#include<string>
#include<string.h>
#include<queue>
#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<thread>
#include<chrono>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<net/if.h>
#include<pthread.h>
#include<fcntl.h>
#include<mutex>
#define ERR printf
#define RTM printf
#define MAXSSDPSIZE 1024

using namespace std;


class CSSDPServer
{
public:
    static  void    Init(  );

protected:
    CSSDPServer(  );
    ~CSSDPServer();
    void    SendMsg(const string&str);
    void    RecvMsg();
    string  MakeSSDHeader();
    string  MakeResponseSearchMsg( );
    static  CSSDPServer *Ins();
    void    InitSendSocket();
    void    InitRecvSocket();
    void    ProcesMsg( const char *strMsg );
private:
    static CSSDPServer *    g_in;
    sockaddr_in             m_sddr ;
    sockaddr_in             m_cddr ;
    int                     m_ssdps;
    int                     m_ssdpc;
    queue<string>           m_que;
    pthread_cond_t          m_cd;
    pthread_mutex_t         m_mt;
};

#endif // CSSDPSERVER_H
/*
 * ===========================================================================
 *
 *       Filename:  SSDPServer.cpp
 *    Description:  服务端设备发现 xml协议
 *        Version:  1.0
 *        Created:  20220406-12:07
 *       Revision:  none
 *       Compiler:  g++
 *         Author:   (zhouyongku),
 *        Company:  
 *
 * ===========================================================================
 */


#include"SSDPServer.h"

CSSDPServer *CSSDPServer::g_in=NULL;
//全局变量


//解析IP地址
CSSDPServer::CSSDPServer(  )
{
    m_mt=PTHREAD_MUTEX_INITIALIZER;
    m_cd=PTHREAD_COND_INITIALIZER;
    RTM("CSSDPServer::CSSDPServer");
    std::thread t([&]
    {
        RTM("CSSDPServer::CSSDPServer create thread");
        InitSendSocket();
        InitRecvSocket();
        char szMsg[MAXSSDPSIZE]={0};
        socklen_t len = sizeof(m_sddr);
        while( true )
        {
            if((recvfrom(m_ssdps, szMsg, MAXSSDPSIZE, 0, (sockaddr*)&m_sddr, &len)) >= 0)
            {
                //printf("recvfrom success ip=%s,msg=%s",inet_ntoa(m_sddr.sin_addr),szMsg);

                ProcesMsg( szMsg );
                memset(szMsg,0,sizeof(szMsg));
            }
            std::this_thread::sleep_for(std::chrono::seconds(10));
        }
    });
    t.detach();
}
void CSSDPServer::ProcesMsg( const char *strMsg )
{
    //and your code here




    SendMsg( MakeResponseSearchMsg( ) );


}


CSSDPServer::~CSSDPServer()
{

}

CSSDPServer *CSSDPServer::Ins(  )
{
    if( CSSDPServer::g_in == nullptr )
    {
        CSSDPServer::g_in = new CSSDPServer(  );
    }

    return CSSDPServer::g_in;
}


void CSSDPServer::InitSendSocket()
{
    struct timeval TimeOut;
    TimeOut.tv_sec = 1;
    TimeOut.tv_usec = 0;


    m_ssdpc=socket(AF_INET,SOCK_DGRAM,0);
    if( m_ssdpc < 0)
    {
        ERR("CSSDPClient::InitSocket failed of create socket 239.255.255.250:1900");
        return;
    }

    bzero(&m_cddr, sizeof(m_cddr));
    m_cddr.sin_family = AF_INET;
    m_cddr.sin_addr.s_addr = inet_addr("239.255.255.250");
    m_cddr.sin_port = htons(1900);
    setsockopt(m_ssdpc, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
    ip_mreq mreq;
    bzero(&mreq,sizeof(mreq));
    mreq.imr_multiaddr.s_addr =inet_addr("239.255.255.250");
    mreq.imr_interface.s_addr =htonl(INADDR_ANY);
    if( setsockopt(m_ssdpc, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) <0 )
    {
        ERR("CSSDPServer::InitSocket failed of setsockopt IP_ADD_MEMBERSHIP");
        return;
    }
}

void CSSDPServer::InitRecvSocket()
{
    RTM("CSSDPServer::InitRecvSocket");
    struct timeval TimeOut;
    TimeOut.tv_sec = 1;
    TimeOut.tv_usec = 0;

    m_ssdps=socket(AF_INET,SOCK_DGRAM,0);
    if( m_ssdps < 0)
    {
        ERR("CSSDPServer::InitSocket failed of create socket 239.255.255.250:1900");
        return;
    }

    bzero(&m_sddr, sizeof(m_sddr));
    m_sddr.sin_family = AF_INET;
    m_sddr.sin_addr.s_addr = INADDR_ANY;
    m_sddr.sin_port = htons(1900);

    if( bind(m_ssdps, (struct sockaddr *)&m_sddr, sizeof(m_sddr)) < 0 )
    {
        ERR("CSSDPServer::InitSocket failed of bind socket 0.0.0.0:1900");
        return ;
    }
    ip_mreq mreq;
    bzero(&mreq,sizeof(mreq));
    mreq.imr_multiaddr.s_addr =inet_addr("239.255.255.250");
    mreq.imr_interface.s_addr =htonl(INADDR_ANY);
    if( setsockopt(m_ssdps, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) <0 )
    {
        ERR("CSSDPServer::InitSocket failed of setsockopt IP_ADD_MEMBERSHIP");
        return;
    }
    RTM("CSSDPServer::InitRecvSocket success");

}

void CSSDPServer::Init( )
{
    CSSDPServer::Ins(  );
}


string CSSDPServer::MakeSSDHeader()
{
    string strMsg="NOTIFY * HTTP/1.1\n"
            "HOST: 239.255.255.250:1900\n"
            "CACHE-CONTROL: max-age=66\n"
            "LOCATION:xml\n"
            "OPT:ns=01\n"
            "01-NLS:0\n"
            "NT: urn:schemas-upnp-org:service:AVTransport:1\n"
            "NTS: ssdp:alive\n"
            "SERVER: Ubuntu/16.04\n"
            "X-User-Agent: redsonic\n"
            "USN:1\n";
    return strMsg;
}


string CSSDPServer::MakeResponseSearchMsg()
{

    string strMsg=MakeSSDHeader();

    char szMsg[MAXSSDPSIZE]={0};

        snprintf( szMsg,MAXSSDPSIZE,
                  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                  "<ProbeMatch>\n"
                  "<mount>your msg</mount>\n"
                  "<enable>your msg</enable>\n"
                  "<platformip>%s</platformip>\n"
                  "<Uuid>your msg</Uuid>\n"
                  "<Types>inquiry</Types>\n"
                  "<DeviceType>CDZSFACEBOX</DeviceType>\n"
                  "<DeviceSN>your msg</DeviceSN>\n"
                  "<DeviceName>your msg</DeviceName>\n"
                  "<MAC>your msg</MAC>\n"
                  "<IPv4Address>your msg</IPv4Address>\n"
                  "<IPv4SubnetMask>your msg</IPv4SubnetMask>\n"
                  "<IPv4Gateway>your msg</IPv4Gateway>\n"
                  "<IPv6Address>::</IPv6Address>\n"
                  "<IPv6Gateway>::</IPv6Gateway>\n"
                  "<IPv6MaskLen>64</IPv6MaskLen>\n"
                  "<DHCP>::</DHCP>\n"
                  "<SoftwareVersion>your msg</SoftwareVersion>\n"
                  "<BootTime>your msg</BootTime>\n"
                  "<Diskrate>your msg</Diskrate >\n"
                  "<Cpurate>your msg</Cpurate >\n"
                  "<Memoryrate>%s</Memoryrate >\n"
                  "</ProbeMatch>");


    strMsg += szMsg;
    return strMsg;
}


//发送ssdp:discover消息
void CSSDPServer::SendMsg(const string&str)
{
    RTM("CSSDPServer::SendMsg msg=%s",str.c_str());

    socklen_t len = sizeof(m_cddr);
    int nLen = sendto(m_ssdps,str.c_str(),str.length(),0,(sockaddr*)&m_cddr,len);
    if( nLen >0 )
    {
        RTM("CSSDPServer::SendMsg success to send to %s msg len=%d",inet_ntoa(m_cddr.sin_addr),nLen);
    }
    else
    {
        RTM("CSSDPServer::SendMsg failed to send msg");
    }
}

void CSSDPServer::RecvMsg()
{
    socklen_t len = sizeof(m_sddr);
    char szMsg[MAXSSDPSIZE]={0};
    int num = recvfrom(m_ssdps,szMsg,MAXSSDPSIZE,0,(sockaddr*)&m_sddr,&len);
    if( num >0 )
    {
        string str;
        //ParseMsg(buf);
    }
}





测试代码:

#include<SSDPClient.h>

int main(int argc, char *argv[])
{
    while( true )
    {
        CSSDPClient::Discover();
        std::this_thread::sleep_for( std::chrono::seconds(10) );
    }

    return 0;
}

#include"SSDPServer.h"
int main(int argc, char *argv[])
{

    CSSDPServer::Init( );


    std::this_thread::sleep_for( std::chrono::seconds(100000) );

    return 0;
}

抓包:

发送方:ssdpclient 192.168.66.44 发送 search命令

接收方 ssdpserv (我在同一个电脑上测试 ip都是192.168.66.44)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值