【p2p、分布式,区块链笔记 UPNP】: 简单服务发现协议 SSDP

  • 在设备加入网络,UPnP发现协议允许设备向控制点广告它的服务。它使用向一个标准地址和端口多址传送发现消息来实现。控制点在此端口上侦听是否有新服务加入系统。为了通知所有设备,一个设备为每个其上的嵌入设备和服务发送一系列相应的发现消息。每个消息也包含它表征设备或服务的特定信息。

SSDP的数据内容格式

  • SSDP (Simple Service Discovery Protocol) 的数据内容格式与HTTP协议格式十分类似。SSDP是一种基于 HTTPU (HTTP over UDP) 的协议,通常用于 UPnP 设备发现。

  • 注:HTTP(HyperText Transfer Protocol)属于计算机网络中的应用层协议。而TCP/UDP是传输层协议。HTTP只是定义了一种信息的描述格式,并不一定要使用TCP进行传输。

  • SSDP是一种基于 UDP 的协议,所以它并不涉及到TCP协议中的三次握手(three-way handshake)来建立连接或者四次挥手(four-segment handshake)来关闭连接。

  • SSDP的操作主要包括发送发现请求(M-SEARCH)、响应发现请求(NOTIFY)以及注册服务(NOTIFY + LOCATION)等操作。它的工作机制主要是基于广播和多播消息,用于通告服务的存在以及查询网络上的服务。

特性 广播 (Broadcast) 多播 (Multicast)
目标主机 网络中的所有主机 特定的一组主机
地址范围 255.255.255.255 (IPv4) 224.0.0.0239.255.255.255 (IPv4)
传输范围 局域网 (LAN) 局域网 (LAN) 和广域网 (WAN)
网络负载 高,因为所有设备都会接收 低,因为只有需要的设备接收
使用场景 ARP、DHCP 等简单网络服务 视频流、股票行情、在线游戏等
跨网络传输 不能跨路由器传播 可以跨路由器传播(需配置)
带宽效率 低,所有主机都接收 高,只有订阅的主机接收
  • 在IPv4环境,当需要使用多播方式传送相关消息的时候,SSDP一般使用多播地址239.255.255.250和UDP端口号1900。

设备和控制点在SSDP中的角色

  • 设备(Device)
    • 被发现的设备,通常是嵌入式设备或网络设备。设备会周期性地发送NOTIFY消息来广播自己的存在。
    • 当收到M-SEARCH请求时,也会响应NOTIFY消息。
  • 控制点(Control Point)
    • 发起服务发现的客户端,通常是PC、手机等。
    • 控制点通过发送M-SEARCH请求来寻找特定的服务。
    • 接收到NOTIFY消息后,控制点就可以对设备进行控制或获取信息。

NOTIFY报文

  • 当设备通过DHCP或其他方式获取IP并加入网络后,会同构239.255.255.250:1900进行多播通告,控制点监听此端口以发现新的服务。
/*!
 * \brief Sends SSDP advertisements, replies and shutdown messages.
 *
 * \return UPNP_E_SUCCESS if successful else appropriate error.
 */
int AdvertiseAndReply(
	/* [in] -1 = Send shutdown, 0 = send reply, 1 = Send Advertisement. */
	int AdFlag,
	/* [in] Device handle. int  value  return by UpnpRegisterRootDevice、 UpnpRegisterRootDevice2、UpnpRegisterRootDevice3 or  UpnpRegisterRootDevice4*/
	UpnpDevice_Handle Hnd, 
	/* [in] Search type for sending replies. */
	enum SsdpSearchType SearchType,
	/* [in] Destination address. */
	struct sockaddr *DestAddr,
	/* [in] Device type. */
	char *DeviceType,
	/* [in] Device UDN. <UDN>uuid:UUID</UDN>  */
	char *DeviceUDN,
	/* [in] Service type. */
	char *ServiceType,
	/* [in] Advertisement age. */
	int Exp);

ssdp:alive

2024-10-05 19:39:29 UPNP-SSDP-2: Thread:0x7F5331C176C0 [.upnp/src/ssdp/ssdp_server.c:878]: Start of received response ----------------------------------------------------
NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=100
LOCATION: http://192.168.19.70:49152/tvdevicedesc.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 3f8c3c84-830e-11ef-b9c9-a0de7ba280b6
NT: urn:schemas-upnp-org:service:tvpicture:1 # 服务类型    
NTS: ssdp:alive  #表示通知消息的子类型
SERVER: Linux/5.15.153.1-microsoft-standard-WSL2, UPnP/1.0, Portable SDK for UPnP devices/17.2.0
X-User-Agent: redsonic
USN: uuid:Upnp-TVEmulator-1_0-1234567890001::urn:schemas-upnp-org:service:tvpicture:1


End of received response ------------------------------------------------------
From host 192.168.19.70
  • 各字段描述见UPnP-arch-DeviceArchitecture-v1.0.pdf的16页。

ssdp:byebye

NOTIFY * HTTP/1.1
   Host: 239.255.255.250:reservedSSDPport
   NT: someunique:idscheme3
   NTS: ssdp:byebye
   USN: someunique:idscheme3

SEARCH报文

  • 控制点向网络广播一个M-SEARCH请求(ssdp:discover)。请求中包含了目标的服务类型。

ssdp:discover

M-SEARCH * HTTP/1.1
   S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6
   Host: 239.255.255.250:reservedSSDPport
   Man: "ssdp:discover"
   ST: ssdp:all  # 查询的目标
   MX: 3
   HTTP/1.1 200 OK
   S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6
   Ext:
   Cache-Control: no-cache="Ext", max-age = 5000
   ST: ge:fridge
   USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
   AL: <blender:ixl><http://foo/bar>

响应消息 (Response)

  • 当设备接收到 M-SEARCH 消息时,如果匹配,会发送一个响应消息来通知请求者设备或服务的存在。控制点接收到NOTIFY消息后,就可以根据设备提供的详细信息,与设备建立连接。
HTTP/1.1 200 OK # 表示成功响应
CACHE-CONTROL: max-age=1800 # 指定响应的有效时间,以秒为单位
DATE: Tue, 20 Oct 2024 10:24:00 GMT # 响应生成的时间戳
EXT:  # 扩展字段,保持空白
LOCATION: http://192.168.1.100:80/upnp/device.xml # 设备的 UPnP 描述文件的 URL 
SERVER: Linux/2.6 UPnP/1.0 MediaTomb/0.12 # 包含设备的操作系统、UPnP 版本以及产品信息
ST: urn:schemas-upnp-org:device:MediaServer:1 # 搜索目标,响应请求中指定的设备或服务类型。
USN: uuid:abcd1234-5678-90ab-cdef-1234567890ab::urn:schemas-upnp-org:device:MediaServer:1 #  唯一服务名称,通常由设备的 UUID 和服务类型组成

代码实现

ST SsdpSearchType

// upnp\src\inc\ssdplib.h#L66-L75
/*! Enumeration to define all different types of ssdp searches */
typedef enum SsdpSearchType
{
	/*! Unknown search command. */
	SSDP_SERROR = -1,
	SSDP_ALL,
	SSDP_ROOTDEVICE,
	SSDP_DEVICEUDN,
	SSDP_DEVICETYPE,
	SSDP_SERVICE
} SType;
// upnp\src\inc\ssdplib.h#L77-L84
#define BUFSIZE (size_t)2500
#define SSDP_IP "239.255.255.250"
#define SSDP_IPV6_LINKLOCAL "FF02::C"
#define SSDP_IPV6_SITELOCAL "FF05::C"
#define SSDP_PORT 1900
#define NUM_TRY 3
#define THREAD_LIMIT 50
#define COMMAND_LEN 300

AdvertiseAndReply函数

// pupnp-branch-1.14.x\upnp\src\ssdp\ssdp_server.c
/*!
 * \brief 发送 SSDP 广告、回复和关闭消息。
 *
 * \return 如果成功返回 UPNP_E_SUCCESS,否则返回适当的错误码。
 */
int AdvertiseAndReply(
	int AdFlag,           /* [in] -1 = 发送关闭消息,0 = 发送回复,1 = 发送广告。 */
	UpnpDevice_Handle Hnd,/* [in] 设备句柄。 */
	enum SsdpSearchType SearchType, /* [in] 发送回复的搜索类型。 */
	struct sockaddr *DestAddr, /* [in] 目标地址。 */
	char *DeviceType,    /* [in] 设备类型。 */
	char *DeviceUDN,     /* [in] 设备 UDN。 */
	char *ServiceType,   /* [in] 服务类型。 */
	int Exp              /* [in] 广告有效期。 */
)
{
   
   
    // 初始化了一些变量
	int retVal = UPNP_E_SUCCESS; long unsigned int i; long unsigned int j;	int defaultExp = DEFAULT_MAXAGE;	struct Handle_Info *SInfo = NULL;	char UDNstr[100];	char devType[100];	char servType[100];	IXML_NodeList *nodeList = NULL;	IXML_NodeList *tmpNodeList = NULL;	IXML_Node *tmpNode = NULL;	IXML_Node *tmpNode2 = NULL;	IXML_Node *textNode = NULL;	const DOMString tmpStr;	const DOMString dbgStr;	int NumCopy = 0;
	memset(UDNstr, 0, sizeof(UDNstr));	memset(devType, 0, sizeof(devType));	memset(servType, 0, sizeof(servType));

	UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Inside AdvertiseAndReply with AdFlag = %d\n",AdFlag);

	/* Use a read lock */
	HandleReadLock(); // 检查设备句柄的有效性。如果句柄无效,则返回错误。
	if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
   
   retVal = UPNP_E_INVALID_HANDLE;		goto end_function;	}
	defaultExp = SInfo->MaxAge;
	
	/* parse the device list and send advertisements/replies */
	while (NumCopy == 0 || (AdFlag && NumCopy < NUM_SSDP_COPY)) {
   
   
		// 资源解析,一般如果发生错误,continue跳过后续步骤,进入下一次循环过程
		if (NumCopy != 0)
			imillisleep(SSDP_PAUSE);
		NumCopy++;
		for (i = 0lu;; i++) {
   
   
			UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Entering new device list with i = %lu\n\n",	i);
			tmpNode = ixmlNodeList_item(SInfo->DeviceList, i);
			if (!tmpNode) {
   
   UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Exiting new device list with i = "       "%lu\n\n",i);
				break;
			}
			dbgStr = ixmlNode_getNodeName(tmpNode);
			UpnpPrintf(UPNP_INFO,API,__FILE__,	__LINE__,"Extracting device type once for %s\n",dbgStr);
			ixmlNodeList_free(nodeList);
			nodeList = ixmlElement_getElementsByTagName((IXML_Element *)tmpNode, "deviceType");
			if (!nodeList) continue;
			UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Extracting UDN for %s\n",dbgStr);
			UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Extracting device type\n");
			tmpNode2 = ixmlNodeList_item(nodeList, 0lu);
			if (!tmpNode2) continue;
			textNode = ixmlNode_getFirstChild(tmpNode2);
			if (!textNode) continue;
			UpnpPrintf(UPNP_ALL,API,__FILE__,__LINE__,"Extracting device type \n");
			tmpStr = ixmlNode_getNodeValue(textNode);
			if (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值