原理:
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 1024using 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 1024using 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 hereSendMsg( 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)