第 14 章:Wi-Fi Direct(P2P)— 临时结伴的旅行团
"不需要旅行社,两个陌生人也能在路上结伴同行——临时推举一个领队,说走就走。"
¶ 本章导读
在传统 Wi-Fi 里,所有设备都要先连上一个 AP(路由器),才能互相通信——就像所有人都必须先到火车站、由车站统一调度才能出发。但很多场景并不需要这个 "车站":手机直接传文件给另一台手机、笔记本直接连打印机、相机直接投屏到电视。
Wi-Fi Direct(Wi-Fi 联盟的认证品牌名,底层技术规范叫 Wi-Fi Peer-to-Peer,简称 P2P)就是为这种 "设备直连" 设计的。它最巧妙的地方在于:不需要真正的 AP,而是让其中一台设备临时 "扮演" AP——这台设备叫 P2P Group Owner(GO,组拥有者),其他设备作为 P2P Client(客户端)连上它。
临时结伴的旅行团 一群驴友在路上偶遇,想结伴同行。他们不需要旅行社(AP),而是当场推举一个人当领队(GO)——领队负责定路线、管钱包、收新人;其他人当团员(Client)。这个临时旅行团(P2P Group)说散就散,下次再遇到还能凭 "老交情" 快速重组(Persistent Group)。
本章覆盖:
- P2P 的角色与拓扑:谁是领队、谁是团员
- 设备发现:两个人在茫茫人海里怎么 "对上眼"(Scan / Find,Listen / Search)
- GO 协商:谁来当领队?(GO Intent + Tie-breaker 的三次握手)
- 组建立与安全:WPS 配对 + 四次握手 + GO 当 DHCP 服务器
- 加入已有的组:自己 Scan 进去 vs 被 Invitation 拉进去
- 持久组:老搭子如何免协商秒重连
- 电源管理:领队如何 "打个盹" 省电(NoA / OppPS)
- 帧格式:P2P IE 与关键属性
本章技术结论依据 Wi-Fi Direct® Specification v2.0(Wi-Fi Alliance, 2025),并标注对应章节;底层 802.11 机制引用 IEEE 802.11-2020。
¶1 P2P 的角色与拓扑
¶1.1 三种角色
| 角色 | 旅行团比喻 | 说明 |
|---|---|---|
| P2P Device | 一个有结伴能力的驴友 | 既能当领队也能当团员,支持 P2P 发现和 WSC(Wi-Fi 简单配置) |
| P2P Group Owner(GO) | 领队 | "类 AP" 实体,提供 BSS 功能与服务,内置 WSC Registrar;可选地为团员之间转发数据、共享上网 |
| P2P Client | 团员 | 实现非 AP STA 功能,担任 WSC Enrollee |
| Legacy Client | 不懂规矩的散客 | 老设备,不懂 P2P 协议,把 GO 当普通 AP 直接连 |
GO 不是真路由器,它只是一台临时扮演 AP 角色的普通设备。组解散后,它又变回普通 P2P Device。
¶1.2 拓扑:1:n 的星形
一个 P2P Group 的拓扑是 1:n——一个 GO 连接多个 Client(§2.2)。这和传统 BSS 一样是星形结构:所有团员都连到领队,团员之间的通信也要经过领队中转(如果 GO 启用了 Intra-BSS Distribution)。
一个 P2P Group 有单一 SSID,提供单一安全域(§2.2)。1:1(n=1)只是 1:n 的特例——两台设备直连。
¶1.3 两个地址:身份证 vs 工牌
P2P 设备有两个 MAC 地址,这是初学者最容易搞混的地方(§2.4.3):
| 地址 | 比喻 | 用途 |
|---|---|---|
| P2P Device Address | 身份证(终身不变) | 唯一标识一个 P2P 设备,用于发现阶段所有帧的接收地址(RA)。是设备全局 MAC 地址(或设置了本地管理位的版本) |
| P2P Interface Address | 临时工牌(组内有效) | 仅用于某个 P2P Group 组内通信。组会话开始前分配,组会话结束即失效 |
身份证(Device Address)证明 "你是谁",全国通用、一辈子不变;工牌(Interface Address)只在 "这家公司"(这个 Group)有效,离职(组解散)就作废。一个人可以同时在多家公司兼职(并发多个 Group),每家发一个不同的工牌。
¶1.4 并发:脚踏两条船
P2P 设备可以同时连着一个传统 WLAN(上网)和一个 P2P Group(直连),这叫 P2P Concurrent Device(§2.3)。例如:手机一边连着家里路由器(5 GHz, 信道 36)刷网页,一边用 Wi-Fi Direct(2.4 GHz, 信道 6)投屏到电视。这需要设备支持多个 MAC 实体。
¶2 设备发现 — 茫茫人海中 "对上眼"
发现是 P2P 最精巧的机制。问题是:两台设备事先都不知道对方在哪个信道,怎么 "碰头"?
P2P Discovery 包含四个部分(§3.1.1):
| 组件 | 作用 |
|---|---|
| Device Discovery | 两台设备汇聚到同一信道,交换设备信息(名称、类型) |
| Service Discovery(可选) | 连接前先发现对方提供什么高层服务 |
| Group Formation | 决定谁当 GO,建立新组 |
| P2P Invitation | 邀请设备加入现有组,或唤起一个持久组 |
¶2.1 先理清:发现里有两件相反的事
初学者读 P2P 发现最容易卡住,是因为没分清两件方向相反的事——而协议里到处都在说 "回不回复 Probe Request",绕得人晕。先把这两件事拎清楚,后面就全通了:
| 我在干什么 | 我发什么帧 | 我收什么帧 | 谁这么干 |
|---|---|---|---|
| 主动找别人 | 发 Probe Request(探测请求) | 收别人回我的 Probe Response | 想找设备的一方 |
| 让别人找到我 | 回 Probe Response(探测响应) | 收别人发来的 Probe Request | 想被发现、想被连的一方 |
所谓 "不回复 Probe Request",意思是我不去应答 "别人" 发来的探测(即我不发 Probe Response、不让自己被这次探测发现)。它不等于"我什么都收不到"——我照样在收别人对我自己那条 Probe Request 的应答(Probe Response)。一个是 "我应答你",一个是 "你应答我",方向正好相反。
为什么一台设备不能两件事同时做?因为 "主动找" 要在多个信道上来回扫 / 发,人是动的;"被找到" 要老老实实停在一个固定信道等别人来探。边走边喊和站着不动等人来,物理上没法同一时刻都干。P2P 的整套发现机制,本质就是在这两个角色之间切换时间片。
理清这点后,下面三个 "状态" 就只是回答了两个问题:
- Scan 阶段回答 "这附近有什么?"——开场先普查一遍。
- Find 阶段的 Listen / Search 回答 "我和那个同样在找的人,怎么撞到同一个信道上?"——靠交替切换两个角色去碰头。
¶2.2 Scan 阶段 — 开场普查
In-band(带内)设备发现的第一步是 Scan 阶段(§3.1.2.1.2)。它直接复用 IEEE 802.11-2020 的标准扫描流程:设备扫遍所有支持的信道,目的有三个——
- 看看周围有没有已存在的 P2P Group(能直接去连,就不用费劲新建组了);
- 看看有哪些传统网络(可与扫 P2P 同时进行);
- 挑出一个最佳工作信道(Operating Channel),万一要自己当 GO 建组时用。
在 Scan 阶段,设备扮演的是上表的第一行:它在发 Probe Request、收各个 GO / 网络回给它的 Probe Response。
所以 "Scan 阶段不回复 Probe Request" 就顺理成章了:此刻我正忙着快速扫遍所有信道找别人,根本没停在任何一个信道上,自然没法当 "被探测的对象" 去应答谁。我是来普查的,不是来摆摊的。
刚到一座陌生城市,你先快步绕城转一圈——看看哪儿有现成的旅行团能搭、哪个广场人最多适合集合(最佳信道)。转圈的时候你只顾看(收别人的招牌),不会停下来支个摊等人来问你(不应答别人的探测)。
¶2.3 Find 阶段 — 两个人怎么 "撞" 到一起
普查完,如果没有现成的组,两台都想直连的设备就要碰头。难点在 14.2.1 已经点破:"边走边找" 和 "停着被找" 没法同时做。Find 阶段的解法是让设备在这两个角色间快速交替循环(§3.1.2.1.3):
| 状态 | 此刻扮演的角色 | 比喻 | 行为 |
|---|---|---|---|
| Listen State(监听) | 让别人找到我 | 站在原地张望 | 停在固定的 Listen Channel(从 1/6/11 里选一个、全程不变),回复 Probe Request → 此刻可被发现 |
| Search State(搜索) | 主动找别人 | 走动着喊话 | 在社交信道 1/6/11 上依次发 Probe Request;此刻不回复别人的 Probe Request(DMG 之外) |
把这张表和 14.2.1 的两件事对上,所有 "回不回复" 就都说得通了:
- Listen State = "停着被找"。我停在固定信道等人来探,所以我回复 Probe Request,让对方发现我。
- Search State = "走着找人"。我正在多个信道间发探测,停不下来,所以不回复别人的探测——和 Scan 一个道理。
为什么非得交替、不能只干一件? 如果两台设备都只顾 "走着找人"(都在 Search),那就是两个人满城乱跑互相喊,谁也没停下来应答,永远撞不上;如果都只 "停着被找"(都在 Listen),那就是两个人各自站在不同广场干等,谁也不去找谁。只有一个恰好在 Listen(停着)、另一个恰好 Search 到了它所在的那个信道(走来探它),才能 "对上眼"。换句话说,交替循环,就是为了制造这个 "一动一静、正好撞上" 的时刻。
¶2.4 社交信道:约定的 "集合广场"
为了让两台设备快速汇聚,P2P 把搜索范围限定在三个 Social Channels(社交信道):2.4 GHz 的信道 1、6、11(DMG/60 GHz 用信道 2)(§3.1.2.1.1)。
DMG 是什么? DMG(Directional Multi-Gigabit,定向多吉比特)指工作频率在 45 GHz 以上的 60 GHz 频段,对应 IEEE 802.11ad/ay(俗称 WiGig)。它频率极高、带宽极大(速率可达数 Gbps),但靠定向波束传输、绕射差、几乎穿不了墙,只适合近距离高速场景。本章规则绝大多数针对常规的 2.4/5 GHz;凡标注 "DMG 之外""DMG 下 " 的地方,都是 P2P 为这个 60 GHz 频段开的特例(比如它用信道 2 而非 1/6/11、加密改用 AES-GCMP、收发探测的规则也略有不同)。初读可直接跳过所有 DMG 分支,只看常规频段那一路即可。
偌大的城市,两个想碰头的人不可能在每条街上找。于是约定 "只在三个广场碰头"——你不在 1 号广场,就去 6 号、11 号转转。范围一小,碰头就快。
¶2.5 随机化:避免 "永远错过"
这里有个精妙的设计。如果两台设备的 Listen 和 Search 完全同步、节奏一致,它们可能永远碰不上(一个喊话时另一个也在喊话,一个张望时另一个也在张望)——就像两个人在门口反复 "你先走 / 不你先走"。
解决办法:Listen State 的持续时间是随机的(§3.1.2.1.3)。每个 Listen State 持续 随机整数 × 100 TU,这个随机整数在 minDiscoverableInterval(默认 1)和 maxDiscoverableInterval(默认 3)之间取值。随机性打破了同步锁步,保证两台设备迟早会在某个社交信道上 "对上眼"。
TU(Time Unit) = 1024 微秒(802.11 标准时间单位)。100 TU ≈ 102.4 毫秒。
¶3 GO 协商 — 谁来当领队?
两台对等的设备碰头后,必须决定谁当 GO(领队)、谁当 Client(团员)。这通过 Group Owner Negotiation(GO 协商)完成——一个三次帧交换(§3.1.4.2)。
¶3.1 三次握手
| 帧 | 方向 | 作用 |
|---|---|---|
| GO Negotiation Request | 发起方 → 对方 | 携带我的 GO Intent、能力、信道列表、Intended Interface Address 等 |
| GO Negotiation Response | 对方 → 发起方 | 回应我的 Intent 与能力,Status=Success 表示同意进入组建立 |
| GO Negotiation Confirmation | 发起方 → 对方 | 确认最终参数(GO/Client 角色、SSID、信道) |
整个 Group Formation(含 GO 协商 + Provisioning)应在 15 秒内完成(§3.1.4.1)。如果发出 GO 协商帧后 100 毫秒内没收到下一帧,发起方就认定协商失败(§3.1.4.2)。
¶3.2 GO Intent:谁更想当领队?
核心是 Group Owner Intent attribute(§4.1.6)——一个表达 "我有多想当 GO" 的值。它只有 1 个字节,但拆成两部分(§4.1.6 Table 31):
| 比特 | 字段 | 取值 | 含义 |
|---|---|---|---|
| Bit 0 | Tie breaker(决胜位) | 0 或 1 | 当双方 Intent 相同时,决定谁当 GO |
| Bit 1–7 | Intent(意愿值) | 0–15 | 越大越想当 GO |
判定规则(§3.1.4.2):
- Intent 值大的一方当 GO。
- Intent = 15 是 "我必须当 GO"——仅当某服务只能在 GO 上正常工作时才设 15(例如提供 Cross Connect(跨接共享上网)的设备)。
- 若双方 Intent 相等且都 < 15:发送 Tie-breaker 位 = 1 的一方当 GO。
- 若双方 都设 15:协商失败,回 "Fail: both P2P Devices indicated an Intent of 15"。此时由 P2P Device Address 较大的一方发送这个失败响应。
选领队就像 AA 制旅行选召集人。Intent 是 "我有多愿意操心"(0 = 完全不想管,15 = 非我不可)。两个人愿意程度一样高时,就靠 "抛硬币"(Tie-breaker 位)定。但如果两个人都坚持 "必须我来"(都填 15),那这趟团就组不成了——谁也不服谁。
¶3.3 Tie-breaker 的细节:为什么平局是 "公平" 的?
平局判定就一条:发出 Tie-breaker 位 = 1 的一方当 GO。但这立刻引出一个尖锐的问题——Tie-breaker 不就是发送方在自己帧里填的一个 bit 吗?那我每次都填 1,岂不是稳赢?
要把这事讲清楚,得分三层看。
第一层:初始位是随机的。 规范规定(§3.1.4.2):发起方第一次发 GO Negotiation Request 时(比如刚开机),Tie-breaker 位应当随机取 0 或 1,且长期来看两个值各占一半。再加上两条规则——
- Response 帧的 Tie-breaker 位,要相对收到的 Request 取反;
- 每次新发(重传不算) Request 时,把自己的位翻转一次。
"取反" 保证了双方的位必然相反(一个 0 一个 1),所以平局判定永远有唯一赢家,不会出现 "两边都填 1" 的死局;"翻转" 则保证万一这轮失败要重试,下一轮的 "硬币" 不会永远是同一面。把初始随机这条接上,平局时发起方当 GO 的概率正好是 50%:
| 发起方初始随机到 | Response 取反后 | 谁的位 = 1 | 结果 |
|---|---|---|---|
| 1 | 0 | 发起方 | 发起方当 GO |
| 0 | 1 | 响应方 | 响应方当 GO |
发起方随机到 1 还是 0 各一半,所以发起方并不占便宜——这是一枚公平的硬币,设计目标就是不偏袒谁。
第二层:这个 "随机" 是君子协定,不是防伪机制。 注意规范的措辞是 "shall be set randomly"——它是对守规矩的实现提的行为要求,而不是协议在机制上强制、不可伪造的随机。这里没有随机数承诺、没有 nonce 交换、没有事后校验,Tie-breaker 就是一个裸露的比特位。所以严格说:
| 情况 | 平局时谁当 GO |
|---|---|
| 双方都按规范、各自掷硬币 | 50/50,发起方不占便宜 |
| 一方耍赖、每次硬填 1 | 它确实能赢下每次平局——协议拦不住 |
第三层:为什么规范敢用这种 "靠自觉" 的设计、不上防伪? 因为 GO 和 Client 之间不是对抗关系——双方目标一致,都想把组建起来,没人想搅黄。而且当 GO 更像苦差而非特权:要扛着不休眠(见 14.7 电源管理)、当 DHCP 服务器、替团员转发流量、可能还得共享上网。真正 "非我当 GO 不可" 的设备(比如要做 Cross Connect(跨接共享上网)的),直接光明正大填 Intent = 15 就行,根本用不着在 Tie-breaker 上做手脚。既然没人有动机去抢着当 GO,一句 "请掷硬币" 的软约定就够用了,犯不上为它设计防伪——这和安全协议(攻击者有强动机伪造)是完全不同的威胁模型。
平局抛硬币,靠的是大家都老实抛。理论上有人能用 "双面都是 1 的硬币" 作弊每次都当召集人——但召集人是出钱出力的苦差,正常人只会抢着推让,没人会作弊去抢这个累活。所以这枚硬币不用防伪,君子协定就够了。
¶4 组建立与安全 — 配对、握手、发 IP
GO 角色确定后,进入 Provisioning(配置) 阶段,本质就是跑一遍 WSC(Wi-Fi Simple Configuration,即 Wi-Fi Protected Setup / WPS)(§3.1.4.3)。
¶4.1 Provisioning = WPS 配对
| 角色 | WPS 身份 | 比喻 |
|---|---|---|
| GO | Internal Registrar(注册方) | 领队,掌管 "入团密码" |
| Client | Enrollee(注册者) | 新团员,提交申请领密码 |
配对方式有三种(§3.1.4,Table 3):
- PIN:一方显示 PIN 码,另一方输入(Display / Keypad)
- PushButton(PBC):两边都按按钮,120 秒 "walk time" 内完成
- NFC:碰一下,带外(Out-of-Band)传递密码
WPS 流程中,Registrar 收到 Enrollee 的 M1("我想入网")后直接回 M2("我来给你发凭证"),不发 M2D(§3.1.4.3)。
M2D 是什么? 它是 WPS 规范里的 M2 的一个变体(D 取自 "不带密钥协商/Diffie-Hellman-less"),含义是 "我收到你的申请了,但现在还没被授权给你发凭证,你先等等"。在开放的 WPS 场景里,一个 Enrollee 发出 M1 后可能被多个 Registrar 听到,那些尚未被用户授权的 Registrar 就回 M2D 婉拒占位。换句话说,M2D 是开放场景里用来" 婉拒占位 " 的稍候信号。但在 P2P 里,GO 协商早已锁定唯一对端,且用户在两台设备上都输了 PIN 或按了按钮——授权在配对开始前就板上钉钉,根本不存在 "还没授权、先挂个稍候牌" 的情况,所以 GO 收到 M1 直接发正式的 M2。
由于 WPS 可能要等用户输入、耗时长达 2 分钟,而 Group Formation 要求 15 秒内完成,所以配对所需信息(如 PIN)必须在协商前就准备好(§3.1.4.1)。
¶4.2 安全:WPA2/WPA3 + 四次握手
组内通信强制采用 WPA2-Personal 或 WPA3-Personal 加密(AES-CCMP;DMG 下用 AES-GCMP)(§3.2.6.1)。关联成功后,GO 与 Client 立即执行 四次握手(GO 当 authenticator,Client 当 supplicant),协商出临时密钥来加密单播和组播帧。
这一套安全机制完全复用 IEEE 802.11-2020 的 RSNA 体系。
这套 "WPA2 或 WPA3" 的强制加密有一个 6 GHz 例外:组一旦跑在 6 GHz 频段,就遵循 Wi-Fi 6 对 6 GHz 的硬性规则——RSNE 不得再声明 WPA2(AKM 套件 00-0F-AC:2/00-0F-AC:6 在 6 GHz 禁用),必须启用 PMF(管理帧保护,MFPR/MFPC 均置 1),组内只能用 WPA3-Personal(SAE)。动机是堵降级:6 GHz 是全新频段、没有老设备包袱,直接禁掉可被降级的 WPA2 最干净(v2.0 §3.5 将 6 GHz 组运行对齐 Wi-Fi 6 Mobile AP/STA 规则)。
¶4.3 GO 当 DHCP 服务器
组建立后还有一个关键角色:GO 必须充当 DHCP 服务器,给连入的 Client 分配 IP 地址(§3.2.6.1)。DHCP 至少要支持 IPv4,分配 IP 地址和子网掩码,且不应下发默认网关(因为这是个孤立的直连组,不是用来上公网的)。唯一例外是 Cross Connect(跨接共享上网):当 GO 一边连着传统 WLAN 上网、一边愿意把这个上网能力 "转手" 给组内团员时,它才会下发默认网关、把团员引向公网(§3.2.6.1)。
领队不光定路线,还要给每个团员发 "队内编号"(IP 地址),方便组内点名互相找。但他不告诉你 "出团去外面世界的那条路"(默认网关)——这个团只管组内活动,不负责带你出团上公网。除非领队自己租了车、愿意顺路把全团拉出去(Cross Connect 共享上网),他才会把 "去外面的路" 也发给你。
¶5 持久组 — 老搭子免协商秒重连
每次都跑完整的发现 + 协商 + WPS 配对很麻烦。Persistent P2P Group(持久组) 解决了重连问题(§3.2.5)。
成功建组后,设备可以存储 P2P Group ID 和 Credentials(凭证)。GO 可以凭此在以后重建同一个组,Client 也能请求唤起持久组。GO 通过把 Persistent Reconnect 位置 1(Group Capability Bitmap 中),声明 "我支持无需用户干预的重连"。
第一次结伴要互相加微信、对暗号(WPS 配对)。但成了 "老搭子" 后,存了彼此的联系方式(Credentials),下次再约直接发个定位就能集合——不用重新加好友。
唤起持久组用的是 P2P Invitation Procedure(邀请流程):把 Invitation Flags 里的 Invitation Type 设为 1,表示 "唤起一个之前建过的持久组"(§3.1.5.1)。这只是邀请流程的用途之一,下一节细讲。
¶6 加入已有的组 — "我去找团" vs "团来拉我"
前面 14.3 讲的 GO 协商,是两台都还没成组的设备从零建组。但现实里更常见的是:组已经在运行了(比如电视已经当 GO 开着投屏组),这时第三台设备要进来。加入一个已存在的组有两个方向相反的路径,初学者常把它们混在一起:
| 方向 | 谁主动 | 谁被动 | 用什么机制 |
|---|---|---|---|
| ① 我去找团(自己加入) | 外部设备 | GO 守在原地 | Scan 发现 + 直接连(§3.1.2.4/§3.2.3) |
| ② 团来拉我(被邀请) | 组内成员 | 外部设备 | P2P Invitation Procedure(§3.1.5) |
¶6.1 方向①:外部设备自己发现并加入
这正是你的直觉——Scan 就能解决 "设备加入已存在的组"。原理是:GO 一旦建好组,就活成了一台 AP。它固定守在 Operating Channel 上周期发 Beacon,安安静静等人来(§3.1.2.4:a GO "stays on the Operating Channel and waits for other devices to discover it")。于是任何想进来的设备:
- Scan 扫信道,收到 GO 的 Beacon / Probe Response,发现 "这儿有个组"(还能从 GO 广播的 Group Info 看到组里已有哪些 Client,§3.2.4);
- 决定加入后,自己发起连接:没配过对就跑 Provision Discovery + WSC 拿凭证,配过就直接关联(§3.2.3)。
这里根本不需要 GO 协商——角色早就定死了(GO 就是 GO),新设备只能当 Client。整个过程跟 "连一个普通 Wi-Fi 热点" 几乎一模一样。
旅行团已经组好、领队举着小旗站在广场(Operating Channel)。想入团的散客自己满广场转(Scan),看到小旗就走过去报名(关联 + 配对)。领队不用挨个去拉人,站着等就行。
¶6.2 方向②:组内成员主动邀请(P2P Invitation Procedure)
但有时候是组里的人想把外面某台设备拉进来——这就得靠 P2P Invitation Procedure。它是个两帧交换(§3.1.5):
| 帧 | 方向 | 作用 |
|---|---|---|
| P2P Invitation Request | 邀请方 → 目标 | "来加入我这个组吧",带上 Group ID、BSSID、信道列表、Operating Channel |
| P2P Invitation Response | 目标 → 邀请方 | Status=Success 表示接受;拒不拒由对方实现策略决定(100 ms 内不回就算失败) |
¶6.2.1 先解决一个前提:邀请方怎么 "够得着" 外面的设备?
这正是你的疑问——组里的人不都守在 operating channel 上吗?外面的设备在别的信道做自己的 Listen/Search,两边信道都不一样,邀请帧怎么递过去?
答案要分清两种 "组内成员",它们的活动自由度完全不同:
- GO:它虽然 "主职" 是守在 operating channel 上发 Beacon 等人,但规范允许它临时离开去别的信道找人——§3.1.2.4 原文:a GO "may search on other channels to find desired devices or services"。也就是说,GO 想邀请谁,就自己切到那个设备所在的信道(对方在做 Listen/Search,能在社交信道上被探到),把 Invitation Request 递过去。它离开期间用 NoA 机制(14.7)提前告诉自家 Client"我下线一会儿",回来再继续发 Beacon。
- Client:团员平时也驻留在 operating channel 上收发数据,但它同样可以临时切走去发现并邀请别人(前提是它的实现支持,且不影响组内业务)。
所以关键认识是:"守在 operating channel" 是组成员的常驻状态,不是禁锢。谁要主动邀请别人,谁就得临时离开本组信道、切到目标所在的信道去递邀请——递完再回来。这跟方向①里 "GO 站着等人" 是对称的:方向①是外人走过来,方向②是组内人主动走出去。
领队平时举着旗站在 3 号广场(operating channel)。但他想拉某个老朋友入团时,会临时托人看着旗、自己跑去朋友所在的 6 号广场喊一嗓子(递 Invitation);喊完再回 3 号广场。出门前他在旗杆上贴张 "我离开一会儿" 的告示(NoA),免得团员找不到他。
¶6.2.2 万一目标设备 "找不到"——GO 中转的唤醒机制
还有一种更棘手的情况:邀请方是个 Client,而它想拉的目标恰好是另一个组里、正在省电休眠的 Client——两个 Client 谁也不直接盯着对方,根本碰不上。这时规范提供了一条经 GO 中转的迂回路(§3.2.4):
- 先拿到目标的地址。 你可能会问:还没 "找到" 目标 Client,怎么会先知道它的 P2P Device Address 去叫醒它?答案是——问它所在组的 GO。GO 平时就在做 Group Information Advertisement:它在自己发的 Probe Response(以及 Beacon)里携带 P2P Group Info attribute,逐个列出组内每个 Client 的描述符——Device Address、设备名、设备类型都在里面(§3.2.4)。所以发起方只要在 GO 的信道上探一下 GO(GO 是醒着的、随时应答),就能把这个组里所有成员的 Device Address 一次性捞回来,哪怕那些 Client 正在睡觉。地址来自 GO 的公开广播,不需要先联系上 Client 本人。
- 拿到地址后,发起方给目标所在组的 GO 发一帧 Device Discoverability Request,附上刚查到的目标 P2P Device Address;
- 那个 GO 收到后,对自家正在打盹的 Client 发一帧 GO Discoverability Request,把它唤醒并叫它保持在线(至少 100 TU);
- GO 再回 Device Discoverability Response 告诉发起方 "它醒了,可以去找它了"。
这之后,发起方才能顺利对目标设备做发现 / 发 Invitation。这套机制专门用来隔着 GO 把一个省电中的 Client"叫醒"——前提是该 Client 在入组时声明了支持 P2P Client Discovery(用 P2P Capability 里的 P2P Client Discovery 位)。
顺带回答一个普遍疑问:其实任何发现场景里,"先知道对方地址" 都不是前提。发现阶段的 Probe Request 用的是广播 / Wildcard(不指定具体 RA),对方应答时才把自己的 Device Address 报出来;而组内成员的地址则由 GO 在 Group Info 里主动公示。换句话说,地址是 "发现的产物",不是 "发现的前提"——你总是先广撒网碰到设备(或问到 GO),才拿到地址,而不是反过来。
你想约的人是另一个团的团员,而他正在帐篷里睡觉、手机静音。你连他叫什么都不知道,于是先找那个团的领队——领队手里有全团花名册(Group Info),随时翻给你看(你不用先认识团员本人)。你照着名册指出 "就找这位",领队过去把人摇醒、让他先别再睡(保持在线),然后回话告诉你 "人醒了,你可以去找他了"。
理清了 "怎么够得着",再看邀请本身的关键点:
- 谁能发邀请? 组内的 GO 或 Client 都能发(§3.1.5.1)。所以不只是 "GO 邀请别人",一个普通团员也能拉人进自己的团。
- Invitation Type 区分两种用途(就是 14.5 提到的那个标志位):
- Type = 0:邀请一台设备加入正在运行的组——这就是你问的 "GO 怎么拉人进来"。
- Type = 1:唤起一个之前建过的持久组(14.5 那个场景)。
- 邀请只是 "开个头"。 目标设备接受后,真正的入组动作还是要走方向①那一套:到 GO 的 Operating Channel 上,没配过对就 Provision Discovery + WSC,配过就直接连(§3.1.5 明确指向 §3.2.3)。换句话说,邀请帧只是把对方 "叫" 到正确的信道、并告诉它该连哪个组,省去了对方满世界 Scan 去碰运气。
方向②是领队(或某个团员)掏出手机给外面的朋友发消息:"我们在 3 号广场,组名叫 XXX,过来吧!" 朋友答应后,照样得走到 3 号广场办入团手续(配对 / 关联)——但他不用自己满城找团了,因为你直接把地址甩给了他。
Scan 是 "我去找团",Invitation 是 "团来拉我"。两条路最后都汇到同一个终点——在 GO 的信道上完成配对与关联。GO 协商(14.3)只发生在 "两台都没成组、要从头建组" 时;只要组已存在,加入就走这两条路之一,不再有 GO 协商。
¶7 电源管理 — 领队如何 "打个盹"
GO 扮演 AP,理论上要一直在线伺候 Client,很费电。P2P 定义了三套省电机制让 GO 能 "打盹"(§3.3,仅适用于 DMG 之外):前两套(OppPS、NoA)是 v1.0 就有的老方案,v2.0 新增了第三套基于 TWT 的省电(见 7.3)。
¶7.1 CTWindow 与 Opportunistic Power Save
**CTWindow(Client Traffic Window)** 是每个 TBTT(信标发送时刻)之后、GO 保证在线的一段时间(§3.3.2)。
Opportunistic Power Save(机会式省电):GO 把 NoA 属性里的 OppPS 位置 1、CTWindow 设为非零值。在每个 CTWindow 结束后,如果 GO 判断所有 Client 都进入了 Doze(休眠)状态,GO 就可以 "见缝插针" 地睡到下一个 TBTT(§3.3.3.1)。
领队规定 "每天早上 8:00–8:30 我一定在帐篷里听候差遣"(CTWindow)。过了这半小时,如果发现所有团员都还在睡觉,领队也就趁机眯一会儿——这就是 "机会式" 省电。
¶7.2 Notice of Absence — 提前公告 "我要离开"
Notice of Absence(NoA,缺席通知) 是更主动的省电:GO 提前在 Beacon / Probe Response 里公告一个 "我将不在场" 的时间表(§3.3.3.2),Client 据此知道这段时间别来找它。
NoA 时间表由 NoA Descriptor 的四个字段定义(§4.1.14, Table 43):
| 字段 | 大小 | 含义 |
|---|---|---|
| Count/Type | 1 octet | 缺席的次数;255 = 持续(无限重复),0 保留不可用 |
| Duration | 4 octets | 每次缺席多长(微秒) |
| Interval | 4 octets | 两次缺席间隔 |
| Start Time | 4 octets | 时间表起始时刻(TSF 低 4 字节) |
最多可同时有 2 个并发的 NoA 时间表(§3.3.3.2)。
领队在帐篷门口贴张告示:"我今天下午 2 点起出去办事,每次离开 30 分钟,每隔 1 小时一趟,共 3 趟。" 团员看到告示就知道什么时候找不到他,自己也趁机休息——大家一起省电。
省电状态优先级(§3.3.3.2,从高到低):
- 非周期 NoA(Count=1)的缺席(最高)
- 从 TBTT 到 Beacon 发送结束的在场
- CTWindow 期间的在场
- 周期 NoA(Count>1)的缺席(最低)
这意味着:无论什么 NoA 时间表,GO 都必须在每个 TBTT 醒来发 Beacon——告示牌本身不能停更。
Client 也可以反向发 P2P Presence Request 影响 GO 的省电节奏(比如 Client 有低延迟流量需求时),GO 用 P2P Presence Response 回应(§3.3.4.4)。
¶7.3 TWT-based 省电 — v2.0 的 "预约制"
v2.0 在 §3.3.3.5 引入第三套方案 TWT-based P2P Power Management:它不再靠 "见缝插针"(OppPS)或 "贴告示"(NoA),而是复用 IEEE 802.11 里的 TWT(Target Wake Time,目标唤醒时间) 机制——GO 在 Beacon / Probe Response 里广播一张 broadcast TWT 时间表(Broadcast TWT ID = 0),并把 Responder PM Mode 位置 1,宣告 "我只在预约的 TWT 服务期(SP)内在线,其他时间别来找我";Client 看到这张表,就知道该在哪个时间窗来敲门。
选择 TWT-based 省电的 GO 不能再同时用 OppPS 或 NoA(三选一,§3.3.3.5)。它的好处是:TWT 是 IEEE 已有的成熟机制,改动最小;时间表还能按需协商、灵活排布不在场时段,比 NoA 的固定周期更自由。GO 会在 v2.0 新增的 P2P Capability Extension 属性(见 14.10)里,把 TWT-based P2P Power Management 位置 1,向对端宣告自己支持这套玩法(§4.1.30, Table 64)。
前两套是 "见缝插针眯一会" 和 "门口贴告示",TWT 则是 "预约门诊"——领队提前排好值班表(broadcast TWT),团员按预约时间来,其余时间领队安心睡觉,双方都不用互相猜。
¶8 帧格式 — P2P IE 与关键属性
P2P 协议不发明新的 802.11 帧类型,而是复用 **Vendor Specific(厂商自定义)** 机制(§4.1.1, §4.2):
- P2P IE(信息元素):基于 Vendor Specific IE,用 Wi-Fi Alliance 的 OUI =
50 6F 9A,OUI Type 指示 P2P。一个 P2P IE 携带一个或多个 P2P 属性。 - 两类 P2P 帧的 Category 不同,别混淆(均 OUI =
50 6F 9A;OUI Type 分两种——P2P IE 用0x09、v2.0 新增的 P2P2 IE 用0x28):- P2P Public Action 帧(用于组建立前的发现 / 协商):基于 Public Action 帧,Category =
0x04、Action field =0x09(§4.2.9,Table 91)。 - P2P Action 帧(用于组内电源管理等):基于 Vendor Specific Action 帧,Category =
0x7F(§4.2.10,Table 116)。
- P2P Public Action 帧(用于组建立前的发现 / 协商):基于 Public Action 帧,Category =
为什么要另起一个 P2P2 IE?v1.0 的 P2P IE 里,属性 ID 只定义到 28(外加 221 留给厂商自定义),这段编号在十余年里被越填越满。v2.0 想装的新能力——P2P Capability Extension(承载 TWT、6 GHz、P2P Pairing 等能力位)、WLAN AP Information、Device Identity Key 等——属性 ID 都落在 29 及以后(§4.1.1, Table 21)。与其硬塞进老 IE 让 v1.0 设备也去解析,不如新开一个 OUI Type 0x28 的 P2P2 IE 专门装它们:老设备看到不认识的 0x28 直接跳过,新设备两个都认——这就是一次干净的向后兼容版本分界。
¶8.1 P2P 属性表(部分)
P2P IE 里可携带的属性(§4.1.1, Table 21):
| ID | 属性 | 用途 |
|---|---|---|
| 0 | Status | 响应帧里的状态码 |
| 1 | Minor Reason Code | 细分原因码 |
| 2 | P2P Capability | 设备能力 + 组能力位图 |
| 3 | P2P Device ID | 目标设备地址 |
| 4 | Group Owner Intent | GO 意愿值 + Tie-breaker |
| 5 | Configuration Timeout | 配置所需时间 |
| 6 | Listen Channel | 监听信道 |
| 11 | Channel List | 支持的信道列表 |
| 12 | Notice of Absence | 缺席时间表 |
| 13 | P2P Device Info | 设备信息(名称、类型等) |
| 14 | P2P Group Info | 组内成员信息 |
| 15 | P2P Group ID | 组标识(Device Address + SSID) |
| 18 | Invitation Flags | 邀请类型 |
| 28 | Persistent Group Info | 持久组信息 |
¶8.2 P2P Public Action 帧(用于发现 / 协商)
承载 GO 协商、邀请、配置发现等组建立前的交互:
| 帧 | 作用 |
|---|---|
| GO Negotiation Request / Response / Confirmation | GO 协商三次握手 |
| P2P Invitation Request / Response | 邀请加入 / 唤起持久组 |
| Provision Discovery Request / Response | 协商 WPS 配置方法 |
| Device Discoverability Request / Response | 发现处于省电的组内 Client |
¶8.3 P2P Action 帧(用于组内管理)
承载组内的电源管理交互(§4.2.10, Table 117):
| OUI Subtype | 帧 |
|---|---|
| 0 | Notice of Absence |
| 1 | P2P Presence Request |
| 2 | P2P Presence Response |
| 3 | GO Discoverability Request |
旅行团里,Public Action 帧是对外打的招呼(发现、协商、邀请,都冲着组外的人),Action 帧则是组内值日表——今天谁值日(NoA 缺席)、几点轮班(Presence Request/Response)、把睡着的叫醒(GO Discoverability Request),都是贴给自家人看的,外人进不来也用不着。
¶9 完整流程串讲
把全章串起来,两台手机用 Wi-Fi Direct 传文件,从发现到加密传输的逐帧过程如下图——这也是用 Wireshark 实际抓包时会看到的帧序列:
读图要点:① 发现分两步——先 Scan(A 扫遍信道、被动收 Beacon 做普查,不应答任何探测),再 Find(双方在社交信道交替 Listen/Search 互探碰头)。①②③ 都在关联之前完成,全部用 P2P Public Action 帧(Category
0x04、OUI50 6F 9A)承载,抓包要开 monitor mode;从 ④ 关联起就回到标准 802.11 流程(关联 → WSC 配对 → 四次握手 → DHCP),和连普通 Wi-Fi 几乎无异。Provision Discovery(②)是可选的——它只是提前约定用哪种 WPS 配对法(谁显示 PIN、谁输入、还是双方按钮),跳过也能直接进 GO 协商。注意这张图画的是从零建组(两台都没成组、需要 GO 协商)。若组已存在、只是有设备要加入,则没有 ③ GO 协商——走 14.6 的两条路之一(自己 Scan 加入,或被 Invitation 拉入),直接从 ④ 关联 / 配对接上。
用文字再串一遍:
1 | 1. 发现 两台手机进入 Find 阶段,在社交信道 1/6/11 上 |
¶10 v2.0 的更新 — 老协议的新装备
本章内容按 Wi-Fi Direct® Specification v2.0(2025) 对齐,相对 v1.0 有几处值得单独拎出来的演进:
- P2P2 IE(OUI Type
0x28):v1.0 只有 P2P IE(0x09),属性 ID 只排到 28;v2.0 新开 P2P2 IE 承载 ID ≥ 29 的新属性(P2P Capability Extension、WLAN AP Information、Device Identity Key 等),老设备自动跳过、新设备两个都认(§4.1.1)。 - TWT 省电:新增第三套省电方案(§3.3.3.5),用 broadcast TWT 时间表代替 OppPS/NoA,见 14.7.3。
- WPA3-Personal:组内加密从 "仅 WPA2-Personal" 升级为 WPA2-Personal 或 WPA3-Personal(SAE)(§3.2.6.1)。
- Cross Connect 共享上网:明确 GO 可把 WLAN 上网能力转手给团员,此时 DHCP 才下发默认网关(§3.2.6.1),见 14.4.3。
除此之外,v2.0 还新增了 6 GHz 频段操作(§3.5)、5 GHz DFS 信道操作(§3.6)、非同步服务发现 USD(§3.7)、P2P Pairing 简化配对(§3.8)、BLE 辅助发现与配对(§3.9)和连接兼容模式 PCC Mode(§3.10)六块能力,它们共同构成 v2.0 相对 v1.0 的 "新装备" 全貌。
其中 USD(§3.7) 解决的是服务发现的同步约束:14.2 的老 Service Discovery 要先完成 Device Discovery、把两台设备汇聚到同一信道才能问 "你提供什么服务"——两步走、还依赖碰头。USD 复用 NAN 的 Service Discovery Frame,让 Seeker 按发布信道清单发 SDF 探询 Advertiser,设备发现与服务发现一帧同时完成,对已守在 Operating Channel 的 GO 更是免了来回切换。
P2P Pairing(§3.8) 则是对 14.4 WSC 一次性配对的补充:WSC 的 PIN/PBC 每次都要现场操作,配对后也不留长期身份。P2P Pairing 引入 DevIK(设备身份密钥)+ DIRA(设备身份解析地址)——配对时把 DevIK 通过安全信道递给对方,之后凭缓存的 DevIK 就能从 DIRA 的 Tag 反解出 "这是哪个老搭子",即便换了设备地址也认得;再走 PASN(DH group 19/20)做免输码的快速重认证,把 "每次对暗号" 升级成 "留了老交情"。
一句话:v2.0 没有推翻 P2P 的老框架,而是用 "新 IE + 新属性 + 新省电 + 更安全的加密" 给它补了装备,老设备依然能入团。
¶11 本章总结
| 机制 | 作用 | 旅行团比喻 |
|---|---|---|
| GO / Client | 一台设备临时扮 AP | 临时推举领队 / 团员 |
| Device / Interface Address | 终身身份 vs 组内临时身份 | 身份证 / 临时工牌 |
| Scan + Find(Listen/Search) | 在社交信道汇聚 | 在约定广场轮流张望与喊话 |
| 社交信道 1/6/11 + 随机化 | 快速碰头、避免锁步 | 只在三个广场碰头、随机错峰 |
| GO Intent + Tie-breaker | 从零建组时决定谁当 GO | 选召集人,平局抛硬币 |
| WPS Provisioning | 配对换凭证 | 加好友、对暗号 |
| 四次握手 + GO 当 DHCP | 加密 + 发组内编号 | 发队内编号,不发回家地址 |
| Scan 加入 / Invitation 邀请 | 加入已存在的组的两条路 | 自己找团 / 团来拉我 |
| Persistent Group | 免协商秒重连 | 老搭子直接约 |
| NoA / OppPS | GO 省电 | 领队提前贴告示去打盹 |
| P2P IE / Action 帧 | 复用 Vendor Specific 承载 | 队内专用暗号 |
v2.0 能力位速查(P2P Capability Extension,§4.1.30 Table 64):
| 能力 | bit | 置 1 含义 |
|---|---|---|
| 6 GHz Band Capable | 4 | 支持在 6 GHz 频段建组 |
| P2P Pairing Capable | 8 | 支持 WPA3 简化配对 |
| TWT-based P2P Power Mgmt | 12 | 支持 TWT 预约制省电 |
Wi-Fi Direct 的精髓是:用一台普通设备临时扮演 AP,让 "设备直连" 既不依赖路由器,又复用了 802.11 成熟的关联与安全机制。 它和下一章的 TDLS 同属 "设备直连",但走了一条彻底抛开 AP 的路;两者对照着读,最能看清 "直连" 的两种思路。
下一章将讲述:P2P 是 "不要 AP 的直连",而 TDLS 偏偏是 "连着 AP 却仍要直连"——都连同一个路由器的两台设备,为什么数据还要绕 AP 中转?怎么抄近路?为什么建立直连的信令反而要" 先借道 AP"?建好之后又怎么切到更空闲的信道提速?