索尼wifi控制相机开发总结(一):ssdp简单总结及qt使用c代码进行ssdp发送与接收

        根据索尼的开发文档, 在使用wifi控制索尼相机的最开始, 需要发送ssdp设备查询消息,然后相机返回的相应的响应消息, 从响应消息中获得相机的设备描述文件(.xml格式)的url.

SSDP简介

        SSDP:Simple Sever Discovery Protocol,简单服务发现协议,此协议为网络客户提供一种无需任何配置、管理和维护网络设备服务的机制。此协议采用基于通知和发现路由的多播发现方式实现。协议客户端在保留的多播地址:239.255.255.250:1900(IPV4)发现服务,(IPv6 是:FF0x::C)同时每个设备服务也在此地址上上监听服务发现请求。如果服务监听到的发现请求与此服务相匹配,此服务会使用单播方式响应。

SSDP包的格式

        设备查询消息格式:

        M-SEARCH * HTTP/1.1

        HOST: 239.255.255.250:1900

        MAN: "ssdp:discover"

        MX: seconds to delay response

        ST: search target

        参数说明:

        M-SEARCH是http协议的扩展方法, 设备查询消息通过该方法发送出去.

        HOST:设置为协议保留多播地址和端口, 必须是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6)

        MAN:设置协议查询的类型, 必须是:ssdp:discover

        MX:设置设备响应最长等待时间, 设备响应在0和这个值之间随机选择响应延迟的值。这样可以为控制点响应平衡网络负载。

        ST: 查询的目标, 它必须是下面的类型:

  ssdp:all 搜索所有设备和服务

  upnp:rootdevice 仅搜索网络中的根设备

  uuid:device-UUID 查询UUID标识的设备

  urn:schemas-upnp-org:device:device-Type:version 查询device-Type字段指定的设备类型,设备类型和版本由UPNP组织定义。

  urn:schemas-upnp-org:service:service-Type:version 查询service-Type字段指定的服务类型,服务类型和版本由UPNP组织定义。

        在索尼的开发文档中, 采用的就是最后一个service的定义.

        在设备接收到查询请求并且查询类型(ST字段值)与此设备匹配时,设备必须向多播地址239.255.255.250:1900回应响应消息:

        HTTP/1.1 200 OK

        CACHE-CONTROL: max-age = seconds until advertisement expires

        DATE: when reponse was generated

        EXT:

        LOCATION: URL for UPnP description for root device

        SERVER: OS/Version UPNP/1.0 product/version

        ST: search target

        USN: advertisement UUID


        各HTTP协议头的含义简介:

        CACHE-CONTROL max-age指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在

        DATE 指定响应生成的时间

        EXT 向控制点确认MAN头域已经被设备理解

        LOCATION 包含根设备描述得URL地址

        SERVER 饱含操作系统名,版本,产品名和产品版本信息

        ST 内容和意义与查询请求的相应字段相同

        USN 表示不同服务的统一服务名,它提供了一种标识出相同类型服务的能力。

        索尼的相机设备描述文件的url会放在LOCATION中, 再通过http GET就可以获取到设备描述符文件.

        然而在实际操作中, 在我发送了M-SEARCH的请求后, 索尼相机并没有向239.255.255.250:1900的多播地址发送响应消息, 通过wireshark抓包后发现, 相机通过udp直接向主机发送了这个响应消息, 可是经过测试, 每次返回的udp消息端口应该都是随机的, 没有想到办法解决, 不过通过抓包发现, 相机会不断向多播地址发送设备通知消息, 主机通过去获取多播地址中的相机设备通知消息, 来得到了相机设备描述文件的url.

        设备通知消息格式:

        NOTIFY * HTTP/1.1

        HOST: 239.255.255.250:1900CACHE-CONTROL: max-age = seconds until advertisement expires

        LOCATION: URL for UPnP description for root device

        NT: search target

        NTS: ssdp:alive

        USN: advertisement UUID

        参数说明:

        HOST 设置为协议保留多播地址和端口,必须是239.255.255.250:1900。

        CACHE-CONTROL max-age指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在

        LOCATION 包含根设备描述得URL地址

        NT 在此消息中,NT头必须为服务的服务类型。

        NTS 表示通知消息的子类型,必须为ssdp:alive

        USN 表示不同服务的统一服务名,它提供了一种标识出相同类型服务的能力。

        从LOCATION中, 就获得了相机的设备描述文件的url.

遇到的问题及解决方法

        首先是在使用从github上下载下来的用来发送和获取ssdp包的.c和.h文件的时候, 直接放到qt工程中是无法编译的, 因为使用的编译方法是不一样的, 必须加上形如:

       extern "C" {

        #include "lssdp.h"

        }

         才能够通过编译, 小技巧, 记下来.


        其次一个问题是双网卡的问题, github下载下来的代码, 原先是通过调用ubuntu系统的socket.h的函数,将socket绑定好了以后, 让操作系统自动分配网卡地址, 并加入该地址的多播组, 如果只是单网卡的话, 这个做法是没有任何问题的, 但如果是多网卡的话, 就往往不能加入指定的网卡地址, 这个情况下, 修改加入多播组的参数, 来加入到指定的网卡多播组中去. 具体修改参数如下:

    if(hasMultiNetworkCard){
        struct in_addr aaaddr = {0};
        aaaddr.s_addr=inet_addr(ip);
        struct ip_mreq imr = {
            .imr_multiaddr.s_addr = inet_addr(Global.ADDR_MULTICAST),
            .imr_interface.s_addr = aaaddr.s_addr
        };
        if (setsockopt(lssdp->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) != 0) {
            lssdp_error("setsockopt IP_ADD_MEMBERSHIP failed: %s (%d)\n", strerror(errno), errno);
            goto end;
        }
    // set IP_ADD_MEMBERSHIP
    }else{
        struct ip_mreq imr = {
            .imr_multiaddr.s_addr = inet_addr(Global.ADDR_MULTICAST),
            .imr_interface.s_addr = htonl(INADDR_ANY)
        };
        if (setsockopt(lssdp->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) != 0) {
            lssdp_error("setsockopt IP_ADD_MEMBERSHIP failed: %s (%d)\n", strerror(errno), errno);
            goto end;
        }
    }

        注意看imr中的interface的ip地址的写入, 如果是写入INADDR_ANY的话, 就会由操作系统默认绑定一个网卡地址, 即不一定是你想要绑定的网卡地址, 因此,在多网卡的情况虾,  由外部手动写入想要绑定的ip地址, 加入该ip的多播组, 从而就能够正确的获取到多播消息. 否则的话, 由于绑定的是另一张网卡的ip地址, 本网卡的ip地址多播组的消息, 会被操作系统认为ip非法而丢弃掉, 从而应用程序不能正确的获取到.

        至此, 就完成了索尼相机控制的第一步, 相机的发现, 获取到了相机的设备描述文件.


参考博客:

ssdp:

https://blog.csdn.net/swanabin/article/details/52024800

多网卡收不到udp组播的问题:

https://blog.csdn.net/little_water/article/details/52550514



                

猜你喜欢

转载自blog.csdn.net/weixin_41793452/article/details/80326702