如何在发送M-SEARCH后持续收听SSDP响应

时间:2015-11-25 13:29:14

标签: upnp ssdp

我想在网络上搜索Sat> IP服务器。周六> IP服务器将其存在广告给其他Sat> IP服务器和客户端。 我不能继续发送M-SEARCH消息,而是它会侦听服务器NOTIFY消息。

在设备的网络设置初始化后,如果已经存在任何有效的Sat> IP服务器,我将发送M-SEARCH消息并获得响应。 但是,我无法得到任何回复,如果我在发送M-SEARCH消息后打开Sat> IP服务器。

这是我的代码。

void SatIP::InitDiscoverThread()
{
    if(INVALID_THREAD_CHK == DiscoverThreadChk)
    {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, PTHREAD_STACK_SIZE);

        printf("InitDiscoverThread pthread_create\n");
        DiscoverThreadChk = PTH_RET_CHK(pthread_create(&DiscoverThreadID, &attr, DiscoverThreadFunc, this));
        if(DiscoverThreadChk != 0)
        {
            ASSERT(0);
        }
    }
}

void SatIP::FinalizeDiscoverThread()
{
    if(INVALID_THREAD_CHK != DiscoverThreadChk)
    {
        printf("FinalizeDiscoverThread pthread_cancel\n");
        pthread_cancel(DiscoverThreadID);
        DiscoverThreadChk = INVALID_THREAD_CHK;
        close(discoverSocket);
    }
}

void *SatIP::DiscoverThreadFunc(void* arg)
{
    SatIP* satip = (SatIP *)arg;
    satip->ListenSSDPResponse();
    pthread_exit(NULL);
}

bool SatIP::SendMSearchMessage()
{
    vSatIPServers.clear();
    FinalizeDiscoverThread();

    const char *searchSatIPDevice = "M-SEARCH * HTTP/1.1\r\n"    \
                                           "HOST: 239.255.255.250:1900\r\n"     \
                                           "MAN: \"ssdp:discover\"\r\n"     \
                                           "MX: 2\r\n"          \
                                           "ST: urn:ses-com:device:SatIPServer:1\r\n\r\n";

    struct sockaddr_in upnpControl, broadcast_addr;
    discoverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (discoverSocket == INVALID_SOCKET)
    {
        printf("socked failed INVALID_SOCKET\n");
        return false;
    }

    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    if(setsockopt(discoverSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)) == SOCKET_ERROR)
    {
        printf("setsockopt timeout failed\n");
        close(discoverSocket);
        return false;
    }

    socklen_t ttl = 2;
    if(setsockopt(discoverSocket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == SOCKET_ERROR)
    {
        printf("setsockopt TTL failed\n");
        close(discoverSocket);
        return false;
    }

    if(setsockopt(discoverSocket, SOL_SOCKET, SO_BROADCAST, searchSatIPDevice, sizeof(searchSatIPDevice)) == SOCKET_ERROR)
    {
        printf("setsockopt broadcast failed\n");
        close(discoverSocket);
        return false;
    }

    upnpControl.sin_family = AF_INET;
    upnpControl.sin_port = htons(0);
    upnpControl.sin_addr.s_addr = INADDR_ANY;
    if (bind(discoverSocket, (sockaddr*)&upnpControl, sizeof(upnpControl)) == SOCKET_ERROR)
    {
        printf("bind failed\n");
        close(discoverSocket);
        return false;
    }

    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_port = htons(1900);
    broadcast_addr.sin_addr.s_addr = inet_addr("239.255.255.250");

    for(int i = 0; i < 3; i++)
    {
        if(sendto(discoverSocket, searchSatIPDevice, strlen(searchSatIPDevice), 0, (sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) == SOCKET_ERROR)
        {
            //printf("sendto failed\n");
            close(discoverSocket);
            return false;
        }
        else
        {
            usleep(10*100);
        }
    }

    InitDiscoverThread();

    return true;
}

void SatIP::ListenSSDPResponse()
{
    while(1)
    {
        char buf[512];
        memset(buf, 0, 512);

        struct sockaddr_in broadcast_addr;
        broadcast_addr.sin_family = AF_INET;
        broadcast_addr.sin_port = htons(1900);
        broadcast_addr.sin_addr.s_addr = inet_addr("239.255.255.250");
        int bcLen = sizeof(broadcast_addr);

        //bool bRet = false;
        while (recvfrom(discoverSocket, buf, 512, 0,  (struct sockaddr*)&broadcast_addr, (socklen_t*)&bcLen) > 0)
        {
            printf("buf:%s\n",buf);
            SATIP_SERVER_DESCRIPTION stServerDesc;
            ostringstream ss;
            if(strstr(buf, "device:SatIPServer"))
            {
                int i = 0;
                char *deviceIp = strstr(buf, "LOCATION:") + 9; // get xml location including server description
                while(deviceIp[i] == ' ') i++; // remove spaces from string
                while(!isspace(deviceIp[i]))
                {
                    ss << deviceIp[i];
                    ++i;
                }
                stServerDesc.location = ss.str().c_str();
                printf("location:%s\n",stServerDesc.location.c_str());

                ss.str(""); // clear ss
                i=0; // clear counter
                deviceIp = strstr(buf, "http://") + 7; // get ip address
                while(deviceIp[i] != ':')
                {
                    ss << deviceIp[i];
                    ++i;
                }
                stServerDesc.ipAddr = ss.str().c_str();
                printf("ipAddr:%s\n", stServerDesc.ipAddr.c_str());

                DownloadDeviceDescription(&stServerDesc);

                stServerDesc.macAddr = GetMACAddressviaIP(stServerDesc.ipAddr);
                printf("macAddr:%s\n", stServerDesc.macAddr.c_str());

                if(IsServerProperToAdd(&stServerDesc))
                    vSatIPServers.push_back(stServerDesc);
                printf("\n");
                //bRet = true;
            }
            memset(buf, 0, 512);
        }
    }
}

如何解决此问题?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:4)

侦听SSDP通知消息与发送M-SEARCH消息无关。即使您没有发送M-SEARCH消息,周六&gt; IP等设备也会定期向239.255.255.250发送NOTIFY消息。因此,您应该加入多播组并从组中接收。

您可以通过将HELLO_PORT更改为1900而将HELLO_GROUP更改为&#34; 239.255.255.250&#34;来使用以下链接中的侦听程序。

http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html

/*
* listener.c -- joins a multicast group and echoes all data it receives from
*       the group to its stdout...
*
* Antony Courtney,  25/11/94
* Modified by: Frédéric Bastien (25/03/04)
* to compile without warning and work correctly
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>


#define HELLO_PORT 1900
#define HELLO_GROUP "239.255.255.250"
#define MSGBUFSIZE 256

main(int argc, char *argv[])
{
     struct sockaddr_in addr;
     int fd, nbytes,addrlen;
     struct ip_mreq mreq;
     char msgbuf[MSGBUFSIZE];

     u_int yes=1;            /*** MODIFICATION TO ORIGINAL */

     /* create what looks like an ordinary UDP socket */
     if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
      perror("socket");
      exit(1);
     }

/**** MODIFICATION TO ORIGINAL */
    /* allow multiple sockets to use the same PORT number */
    if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) {
       perror("Reusing ADDR failed");
       exit(1);
       }
/*** END OF MODIFICATION TO ORIGINAL */

     /* set up destination address */
     memset(&addr,0,sizeof(addr));
     addr.sin_family=AF_INET;
     addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
     addr.sin_port=htons(HELLO_PORT);

     /* bind to receive address */
     if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
      perror("bind");
      exit(1);
     }

     /* use setsockopt() to request that the kernel join a multicast group */
     mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
     mreq.imr_interface.s_addr=htonl(INADDR_ANY);
     if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {
      perror("setsockopt");
      exit(1);
     }

     /* now just enter a read-print loop */
     while (1) {
      addrlen=sizeof(addr);
      if ((nbytes=recvfrom(fd,msgbuf,MSGBUFSIZE,0,
               (struct sockaddr *) &addr,&addrlen)) < 0) {
           perror("recvfrom");
           exit(1);
      }
      puts(msgbuf);
     }
}