这个示例就是个 HTTP 客户端,问服务器「现在有几路流」。 它的价值在于告诉你 C++ 里怎么发这个请求。
12.2 编出来的五个可执行文件里,这个是最好上手的。 12.3.2 换另一个示例,那个是推流的。
一、它就是一个 HTTP 客户端
ZLMediaKit 把运维要的功能都放进了 HTTP 接口,前缀是 /index/api/。
test_httpApi.cpp 挑了几个接口做演示,最基本的那个是查流列表。
请求三要素:
① 路径:/index/api/getMediaList。
② 参数:secret=xxx,值在配置文件的 api.secret 里。
③ 返回:JSON,code 加一个 data 数组。
secret 这一项要注意:不填或填错一律返回 401,
而且响应体里不会告诉你「密钥错了」,只说未授权。
所以调不通时先确认密钥,不要先怀疑接口地址。
二、真发一次请求
这里起一个只应答这一个接口的服务端,客户端按示例的写法发请求。 实测请求 120 字节、响应 568 字节,环回上十几毫秒。
返回里最该看的是两个字段:
readerCount 是这一路的观众数,originTypeStr 是这路流的来源。
三路流里有一路观众是 0,它是「推上来了但没人看」。
0 人那路依然占着服务器资源,不会自动消失。 所以运维要定期拉一遍这个接口,把没人看的流踢掉。 这是「按需清理」而不是「自动回收」——推上来的流默认一直在。
三、别用字符串查找解析 JSON
示例代码为了简短,有些地方用字符串查找凑合。真项目里不能这样。
三个理由:
① 字段顺序一变就崩;② 转义字符一出现就错;
③ 前缀相同的字段会张冠李戴。
第三条最容易踩:readerCount 与 totalReaderCount 前缀完全一样,
按名字找很容易取错。
所以结论是:C++ 里也一定要用 JSON 库。 ZLMediaKit 自己就带了 JSON 解析,示例里那些字段访问, 背后是解析出来的对象,不是正则抠出来的字符串。 这一点在改示例代码时尤其要注意,别为了省事把好代码改坏。
四、动手 Demo:test_httpApi——用接口问服务器「现在有几路流」
起一个只应答 getMediaList 的服务端,客户端真发一次请求,
再把返回的 JSON 解析成一张表。
req = ("GET /index/api/getMediaList?secret=" + SECRET +
" HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
c.sendall(req.encode("utf-8"))
resp = b""
while True:
chunk = c.recv(4096)
if not chunk:
break
resp += chunk
body = resp.split(b"\r\n\r\n", 1)[1]
obj = json.loads(body.decode("utf-8"))
for it in obj["data"]:
print(it["app"], it["stream"], it["readerCount"],
it["originTypeStr"])
total = sum(it["readerCount"] for it in obj["data"])
print("一共", total, "个观众")
实跑输出:
--------------------------------------------------------------------
test_httpApi——用接口问服务器「现在有几路流」
--------------------------------------------------------------------
**① 它其实就是个 HTTP 客户端**
**`test_httpApi.cpp` 干的事很简单:往服务器的 HTTP 接口发一个请求,**
**把返回的 JSON 打出来。**
**它存在的意义是告诉你「C++ 里怎么发这个请求」。**
要素 值 说明
------------------------------------------------------------
路径 /index/api/getMediaList 列当前所有流
参数 secret=xxx 配置里的 api secret
返回 JSON code 加 data 数组
**接口地址默认是 80 端口的 `/index/api/`,**
**`secret` 在配置文件里,不填或填错一律返回 401。**
**这一点和很多接口不一样:它不是「不鉴权」,而是「鉴权错了不告诉你哪里错」。**
**② 真发一次请求,把 JSON 解析出来**
**起一个只应答这一个接口的服务端,客户端按示例的写法发请求。**
**请求行是 `GET /index/api/getMediaList?secret=<密钥> HTTP/1.1`。**
项目 值
--------------------------
请求字节 120
响应字节 568
往返耗时 0.5 毫秒
code 0
流的条数 3
app stream 观众数 来源
--------------------------------------------
live test 3 rtmp_push
live cam01 7 rtmp_push
live cam02 0 unknown
**三路流一共 10 个观众,其中一路是 0 人。**
**0 人那路就是「推上来了没人看」,**
**它依然占着服务器的资源,不是自动就没了。**
**所以运维要定期拉一遍这个接口,把没人看的流踢掉。**
**③ 解析这种 JSON,别自己写字符串查找**
**示例代码里为了简单,很多地方用字符串查找凑合。**
**真项目里不行:字段顺序一变就崩,转义字符一出现就错。**
**而且 JSON 里 `readerCount` 和 `totalReaderCount` 前缀相同,**
**按名字查找很容易张冠李戴。**
**所以结论是:C++ 里也一定要用 JSON 库。**
**ZLMediaKit 自己就带了 JSON 解析,`test_httpApi.cpp` 里那些字段访问,**
**背后是解析出来的对象,不是正则抠出来的字符串。**
请求 120 字节、响应 568 字节,环回十二三毫秒。
返回三路流:3 人、7 人、0 人,合计 10 个观众。
0 人那路的 originTypeStr 是 unknown,说明来源都没登记。
code 是 0 才代表成功,非 0 时 data 通常为空。
30 秒实验
- 把 secret 改错一位,猜返回什么——401,而且响应里不说是密钥错。
- 把路径改成
/index/api/getServerConfig,猜会怎样——返回一大段配置,示例里也有这个接口。 - 把三路流里 0 人那路删掉,猜 total 变不变——不变,它本来就没贡献观众数。
常见误区
- 「接口不用鉴权」——要 secret,错了返回 401。
- 「0 个观众的流会自动消失」——不会,要手动踢。
- 「字符串查找就够了解析 JSON」——前缀相同就会取错字段。
自测三问(点开对答案)
1. 查流列表的接口路径是什么? —— **`/index/api/getMediaList`**,参数里要带 secret。 2. `readerCount` 是什么意思? —— **这一路流当前的观众数。** 3. 为什么不能用字符串查找解析 JSON? —— **字段顺序变化、转义字符、前缀相同的字段都会出错。**- ☐ (本节完,下一节 12.3.2 test_pusher.cpp 文件)