RtspServer:轻量级RTSP服务器和推流器
文章目录
- 项目概述
- 技术分析
- 支持的编码格式
- 传输方式
- 心跳检测机制
- RTSP 推流
- 安全性
- 架构分析
- RtspServer 整体架构
- 流程分析
- 1. 客户端连接和会话建立
- 2. 媒体数据传输
- 3. 心跳检测和连接维护
- xop 基础库
- 项目介绍
- 功能特性
- xop 整体架构
- 应用场景
- 社区问题收集与解答
- 问题一:刚开始播放时有些花屏
- 问题二:推送 H.265 流时播放器无法播放
- Bug 修复与代码分析
- 问题描述
- 分析与解决方案
- 1. 修改 RTP 包的最大负载大小
- 2. 调整 RTP TCP 头部大小
- 3. 修正 H.264 视频源的帧处理
- 4. 修正 H.265 视频源的帧处理
- 总结
- 进一步的技术分析
- 深入理解 RTP 分片与 NALU 处理
- H.264 中的 FU-A 分片
- H.265 中的 FU 分片
- 起始码的处理
- 播放器兼容性
项目概述
RtspServer 是由 PHZ76 开发的高效、可定制的实时流媒体服务器解决方案。它基于作者编写的网络基础库 xop,允许开发者轻松处理和分发实时音视频流。项目提供了一个名为 DesktopSharing 的示例应用,可以捕获桌面和麦克风声音,并在编码后通过 RTSP 协议进行转发和推流。该项目支持 Windows 和 Linux 平台,代码量少,相比于 live555 等经典的流媒体库,RtspServer 更加轻量级,易于集成和二次开发,已在公司的项目中应用。
技术分析
支持的编码格式
RtspServer 支持多种音视频编码格式,涵盖了广泛的应用场景:
- 视频编码:H.264、H.265
- 音频编码:G.711A、AAC
传输方式
-
单播 (Unicast):
- RTP_OVER_UDP:通过 UDP 协议传输 RTP 数据,延迟低,但在网络不稳定时可能会丢包。
- RTP_OVER_RTSP (TCP):通过 RTSP 协议在 TCP 连接上传输 RTP 数据,可靠性高,适用于防火墙或 NAT 环境。
-
组播 (Multicast):
- 适用于需要将同一流媒体数据发送给多个客户端的场景,节省网络带宽。
心跳检测机制
针对单播传输,RtspServer 内置了心跳检测机制,可以及时发现和处理连接异常,确保数据传输的稳定性。
RTSP 推流
RtspServer 支持 RTSP 推流功能,使用 TCP 协议进行数据传输,保证了数据的可靠传输,适用于对传输可靠性要求较高的应用场景。
安全性
RtspServer 内置了摘要认证(Digest Authentication),为服务提供了安全保障,防止未经授权的访问。
架构分析
RtspServer 整体架构
RtspServer 的架构主要包括以下组件:
- RTSP Server:处理客户端的 RTSP 请求,包括 SETUP、PLAY、PAUSE、TEARDOWN 等指令,管理会话和媒体流。
- Media Session:表示一个媒体会话,包含媒体流的相关信息,如流名称、媒体类型、编码格式等。
- Media Source:媒体源,负责提供音视频帧数据,可以来自文件、摄像头、麦克风或其他实时数据源。
- RTP Connection:负责通过 RTP 协议发送音视频数据,支持单播和组播传输。
流程分析
1. 客户端连接和会话建立
- 客户端通过 RTSP 协议发送连接请求到服务器。
- RTSP Server 接收到请求后,解析并创建一个新的 Media Session。
- 为每个媒体流(音频或视频)创建对应的 Media Source。
2. 媒体数据传输
- Media Source 获取音视频数据帧(可能来自编码器或实时采集设备)。
- RTP Connection 负责将媒体数据打包成 RTP 包,通过网络传输给客户端。
- 支持的传输方式包括 RTP_OVER_UDP、RTP_OVER_RTSP 和组播。
3. 心跳检测和连接维护
- 为了保持连接的稳定性,服务器会定期发送心跳检测,确认客户端的在线状态。
- 如果检测到连接异常,服务器会及时释放资源,防止资源泄漏。
xop 基础库
项目介绍
xop 是 RtspServer 的基础网络库,参考了 muduo 和 live555 的设计,封装了一个简单高效的网络框架,提供了构建高性能网络应用的基础组件。
功能特性
- 跨平台支持:兼容 Windows 和 Linux 操作系统。
- Windows 下:使用
select
实现事件循环。 - Linux 下:使用
epoll
实现高效的事件通知机制。
- Windows 下:使用
- 事件驱动模型:基于 Reactor 模式,实现非阻塞 IO 和事件驱动。
- 定时器:提供高精度的定时任务调度。
- 内存管理:实现了环形缓冲区和内存池,提升内存分配和释放的效率。
- 日志系统:内置简洁的日志功能,方便调试和运行监控。
xop 整体架构
- EventLoop:事件循环,核心组件,负责监听和分发 IO 事件、定时器事件等。
- Channel:通道,封装了文件描述符及其感兴趣的事件类型,如可读、可写等。
- TimerQueue:定时器队列,管理所有的定时任务。
- Buffer:缓冲区,提供高效的数据读写接口。
- Acceptor:监听器,负责接受新的客户端连接。
应用场景
- 在线教育平台:提供高清、流畅的远程教学体验,实现教师与学生的实时互动。
- 视频监控系统:实时传输监控摄像头的视频流,实现远程监控和安全管理。
- 远程会议系统:确保音频和视频的同步传输,提高远程会议的质量和效率。
- 桌面共享与远程协助:通过 DesktopSharing 示例应用,实现桌面实时共享和远程协助功能。
社区问题收集与解答
问题一:刚开始播放时有些花屏
问题描述:
在播放开始时,视频出现花屏现象。怀疑是与 GOP(Group of Pictures)有关,没有收到完整的一组 GOP,所以会花屏。是否应该等待下一个 I 帧?
解答:
是的,花屏问题通常是由于解码器没有接收到完整的关键帧(I 帧)导致的。解码器需要从 I 帧开始才能正确解码后续的 P 帧和 B 帧。
解决方案:
- 确保首帧为关键帧:在客户端连接后,服务器应确保发送的第一帧是关键帧。可以在编码器中设置,让其在新的会话开始时立即生成一个 I 帧。
- 检查帧类型:在编码过程中,可以通过
AVFrame
或其他编码库的接口,获取帧类型,根据编码后的类型(I 帧、P 帧、B 帧)进行处理。 - 缓冲策略:在客户端实现一定的缓冲策略,等待接收到第一个 I 帧后再开始解码和播放。
问题二:推送 H.265 流时播放器无法播放
问题描述:
在使用 H.265 推流时,播放器无法正常播放视频。怀疑是在 H265Source
的 handleFrame
函数中处理有误。
解答:
问题可能出在对 NALU(Network Abstraction Layer Unit)的处理上。在 H.265 码流中,每个 NALU 前通常有一个起始码(如 0x00 00 00 01
)。在计算 NALU 类型时,需要正确跳过起始码。
解决方案:
-
去除起始码:在处理帧数据时,需要跳过起始码,以正确解析 NALU 类型。
uint8_t *frameBuf = frame.buffer.get(); uint32_t frameSize = frame.size; frameBuf += 4; // 跳过起始码 frameSize -= 4; // 调整帧大小
-
统一处理方式:建议在构建
VideoFrame
时就去掉起始码,这样后续的处理会更加统一。 -
区别转发和推流:对于转发和推流的处理,可以有所区别。直接获取 H.265 帧数据后调用
pushFrame
,确保计算时是带有起始码的头部信息。
注意:
-
NALU 解析:正确解析 NALU 类型对于视频解码非常重要。确保在处理时准确跳过起始码,才能正确识别帧类型。
-
播放器兼容性:不同的播放器对码流格式的要求可能不同,确保码流符合标准,有助于提高兼容性。
Bug 修复与代码分析
问题描述
在使用 RtspServer 时,发现使用 VLC 和 ffplay 播放时出现错误:
Invalid NAL unit 0, skipping
导致播放器无法正常播放视频流。需要对代码进行修复,解决该问题。
分析与解决方案
经过分析,问题出在 RTP 包的组装和 NALU 的处理上。以下是具体的修复步骤和代码修改。
1. 修改 RTP 包的最大负载大小
文件:rtp.h
问题分析:
- 原始定义的
MAX_RTP_PAYLOAD_SIZE
为 1420,可能导致 RTP 包过小,增加了网络负载。 - RTP 头部的大小为 12 字节,实际可用的负载大小应该更大。
修改内容:
// 原始定义
#define MAX_RTP_PAYLOAD_SIZE 1420// 修改为
#define MAX_RTP_PAYLOAD_SIZE 1440
解释:
- 将
MAX_RTP_PAYLOAD_SIZE
增加到 1440,可以减少 RTP 包的数量,提高传输效率。 - 参考了 Live555 中的
MultiFramedRTPSink.cpp
,调整了最大负载大小。
2. 调整 RTP TCP 头部大小
文件:rtp.h
问题分析:
- 原代码中
RTP_TCP_HEAD_SIZE
定义为 4,当传输模式为RTP_OVER_UDP
时,这个值应该为 0。 - 在 UDP 传输中,不需要额外的 TCP 头部。
修改内容:
// 原始定义
//#define RTP_TCP_HEAD_SIZE 4 // when transport_mode_ = RTP_OVER_TCP// 修改为
#define RTP_TCP_HEAD_SIZE 0 // when transport_mode_ = RTP_OVER_UDP
解释:
- 将
RTP_TCP_HEAD_SIZE
设置为 0,确保在 UDP 传输时,不会错误地加入额外的头部。
3. 修正 H.264 视频源的帧处理
文件:H264Source.cpp
问题分析:
- 在处理 H.264 帧时,需要正确跳过起始码(
0x00 00 00 01
),否则会导致 NALU 解析错误。 - 当帧大小小于等于最大 RTP 负载时,直接发送整个帧;否则,需要进行分片(FU-A)。
修改内容:
if (frame_size <= MAX_RTP_PAYLOAD_SIZE)
{frame_buf += 4; // 跳过起始码frame_size -= 4;// 构建 RTP 包RtpPacket rtp_pkt;rtp_pkt.type = frame.type;rtp_pkt.timestamp = frame.timestamp;rtp_pkt.size = frame_size + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE;rtp_pkt.last = 1;...
}
else
{frame_buf += 4; // 跳过起始码frame_size -= 4;char FU_A[2] = {0};FU_A[0] = (frame_buf[0] & 0xE0) | 28; // NALU 头部FU_A[1] = 0x80 | (frame_buf[0] & 0x1F); // FU-A 起始标志// 进行分片处理
}
解释:
- 对于小于等于最大负载的帧,直接跳过起始码,发送整个 NALU。
- 对于大于最大负载的帧,跳过起始码,进行 FU-A 分片,需要正确构建 FU-A 头部。
4. 修正 H.265 视频源的帧处理
文件:H265Source.cpp
问题分析:
- H.265 的 NALU 解析与 H.264 类似,但头部格式不同,需要相应调整。
- 同样需要正确处理起始码和分片。
修改内容:
if (frame_size <= MAX_RTP_PAYLOAD_SIZE)
{frame_buf += 4; // 跳过起始码frame_size -= 4;// 构建 RTP 包RtpPacket rtp_pkt;rtp_pkt.type = frame.type;rtp_pkt.timestamp = frame.timestamp;rtp_pkt.size = frame_size + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE;rtp_pkt.last = 1;...
}
else
{frame_buf += 4; // 跳过起始码frame_size -= 4;uint8_t PL_FU[3] = {0};uint8_t nalUnitType = (frame_buf[0] & 0x7E) >> 1;PL_FU[0] = (frame_buf[0] & 0x81) | (49 << 1); // NALU 头部PL_FU[1] = frame_buf[1]; // NALU 头部PL_FU[2] = 0x80 | nalUnitType; // FU 起始标志// 进行分片处理
}
解释:
- 同样跳过起始码,正确调整
frame_buf
和frame_size
。 - 构建 H.265 的分片头部,确保 NALU 类型和标志位正确。
总结
通过上述修改,修复了播放器无法正常播放的问题。主要原因在于:
- 起始码处理:需要正确跳过起始码,才能正确解析 NALU。
- RTP 包组装:调整最大负载大小,确保网络传输的效率和稳定性。
- 分片处理:在帧过大时,正确地进行 RTP 分片,构建正确的 FU-A(H.264)或 FU(H.265)包。
这些修改使得 RtspServer 在与常见播放器(如 VLC、ffplay)兼容性方面得到了改善。
进一步的技术分析
深入理解 RTP 分片与 NALU 处理
在 RTP 传输中,当单个 NALU 的大小超过了最大 RTP 负载大小时,需要对其进行分片。分片时,需要构建特殊的 RTP 包,称为 Fragmentation Unit(FU)。
H.264 中的 FU-A 分片
-
FU-A 头部格式:
+---------------+ |0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+ |S|E|R| Type | +---------------+
-
S(Start):分片的开始标志位,1 表示这是第一个分片。
-
E(End):分片的结束标志位,1 表示这是最后一个分片。
-
Type:原始 NALU 的类型。
H.265 中的 FU 分片
-
FU 头部格式:
+---------------+ |0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+ |S|E| Type | +---------------+
-
S(Start):分片的开始标志位。
-
E(End):分片的结束标志位。
-
Type:原始 NALU 的类型。
起始码的处理
- 起始码(Start Code):在原始的 H.264/H.265 码流中,起始码用于分隔 NALU,一般为
0x00 00 00 01
。 - RTP 传输中不需要起始码:在 RTP 传输中,起始码不应包含在传输的数据中,需要在发送前去除。
- 解析 NALU 类型:跳过起始码后,才能正确解析 NALU 的头部,获取 NALU 类型。
播放器兼容性
-
VLC 和 ffplay 等播放器在解析 RTP 流时,严格遵循标准。如果发送的数据不符合 RTP 和 H.264/H.265 的封装规范,就会出现错误。
-
错误信息:
Invalid NAL unit 0, skipping
表示接收到的 NALU 类型为 0,这是无效的,可能是因为没有正确跳过起始码导致的。
相关文章:

RtspServer:轻量级RTSP服务器和推流器
文章目录 项目概述技术分析支持的编码格式传输方式心跳检测机制RTSP 推流安全性 架构分析RtspServer 整体架构流程分析1. 客户端连接和会话建立2. 媒体数据传输3. 心跳检测和连接维护 xop 基础库项目介绍功能特性xop 整体架构 应用场景社区问题收集与解答问题一:刚开…...
为什么结构化 Prompt 如此有效?
你好,我是三桥君 在今年,我研究了结构化编写Prompt的方法,并观察到这种结构化、模板化的Prompt能够有效地突破ChatGPT 3.5的限制,实现所谓的“越狱”。然而,为什么ChatGPT会对这种结构化Prompt如此有效呢?…...

无人机飞手培训校企合作特训技术详解
随着无人机技术的飞速发展,其在航拍、农业、测绘、救援等多个领域的应用日益广泛,市场对高素质无人机飞手的需求急剧增加。为满足这一需求,促进教育与产业深度融合,无人机飞手培训校企合作模式应运而生。本文将从确定合作目标、共…...

从零开始的软件开发详解:数字药店系统源码与医保购药APP
很多小伙伴们疑问,医保购药APP是如何开发的,今天我将从零数字药店系统源码开始为大家提供一条清晰的实现方案。 一、技术架构设计 在开发医保购药APP之前,首先需要明确技术架构。一般来说,APP的技术架构可以分为前端和后端。 1…...

【记录】在返回值类型为BigDecimal情况下末尾小数位为0的会省略不显示
【问题】:在返回值类型为BigDecimal情况下末尾小数位为0的会省略不显示 问题复现: 实体类 package com.zlp.aspect.entity;import java.math.BigDecimal;/*** program: my_utils* description:* author: zlp* create: 2024-09-24 10:01**/public clas…...

通信工程高级职称评审条件详细解读
通信工程只有正高和副高级别的职称,中级通信工程的职称是需要自己参加考试的,并不是评审获得,这个大家需要注意一下,先要考取中级通信工程师之后才能评审副高和正高级通信工程的职称。 下面跟甘建二一起来看看通信专业职称评审条件…...

yolov8环境安装
yolov8 git地址 https://github.com/ultralytics/ultralytics/tree/main 我的电脑显卡配置 nvidia-smi cuda11.8下载 https://developer.nvidia.com/cuda-11-8-0-download-archive?target_osWindows&target_archx86_64&target_version10&target_typeexe_local …...

C语言中易混淆概念的关键字
最快的关键字---- register register: 这个关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内 存寻址访问以提高效率。注意是尽可能,不是绝对。你想想,一个 CPU 的寄存器也就那么 几个或几十个,你要是定义了很多很…...

网络资源模板--Android Studio 零食工坊(商城)
目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--零食工坊 二、项目测试环境 三、项目详情 1. **加载 Fragment 布局**: - 使用 inflater.inflate 加载 fragment_snack 的布局。 2. **视图组件初始化**&am…...
百度高德坐标系相互转换
一、百度与高德坐标系互换 百度坐标系:bd09 高德坐标系: gcj02 import math# 坐标系转换:百度转高德,高德转百度 class CoordinateSystem:staticmethoddef bd09_to_gcj02(bd_lon, bd_lat):百度坐标系:bd09 转成高德坐标系 gcj0…...

免费下载6组简历模板,让HR一眼相中你!
简历是求职者向招聘单位展示自我的重要工具,选择一份高质量的简历模板免费下载,能够提升面试机会并留下深刻印象。优秀的简历模板需具备清晰的信息结构、出色的视觉效果及合理的排版布局。简历的配色可以凸显求职者的个性,而有逻辑性的排版则…...
设计模式之模版方法模式
定义 定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。模版方法使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定…...

解析!文档扫描 SDK 中的高级图像处理技术
随着世界数字化,文档扫描已成为现代商业运营的关键,它使文档的存储、访问和管理更加便捷。然而,扫描图像的质量对于这些数字档案的有效性至关重要。高质量的扫描可确保文本清晰、数据准确捕获并且信息易于检索。 另一方面,质量差…...
线性判别分析(LDA)中求协方差矩阵示例
让我们通过一个简单的例子计算协方差矩阵。假设我们有两类数据集 X 0 X_0 X0 和 X 1 X_1 X1,每类有两个样本,每个样本有两个特征。 数据集: 类 0 的样本: X 0 [ 1 2 2 3 ] X_0 \begin{bmatrix} 1 & 2 \\ 2 & 3…...

Maven的详细解读和配置
目录 一、Maven 1.1 引言 1.2 介绍 1.3 下载安装 1.3.1 解压 1.3.2 配置环境变量 1.3.3 测试 1.4 仓库[了解] 1.5 Maven配置 1.5.1 修改仓库位置 1.5.2 设置镜像 二、IDEA - MAVEN 2.1 idea关联maven 2.2 为新项目设置 2.2 创建java项目[重点] 2.3 java项目结构…...
在vue2项目中使用dart-sass
问题描述 在vue2项目中使用dart-sass。或者将node-sass换成dart-sass,原因是node-sass安装比较困难,很多时候sass-loader无法安装成功。另外在win和Linux环境下运行行项目需要针对Linux搞一个node-sass的Linux版本。 问题分析 在 Vue 2 项目中使用 da…...

【机器学习】过拟合与欠拟合——如何优化模型性能
【机器学习】过拟合与欠拟合——如何优化模型性能 1. 引言 在机器学习中,模型的表现不仅依赖于算法的选择,还依赖于模型对数据的拟合情况。过拟合(Overfitting)和欠拟合(Underfitting)是模型训练过程中常…...
二进制日志gtid模式
# --skip-gtids,使用mysqlbinlog截取时添加该参数,会执行已经执行的事务 mysqlbinlog --skip-gtids --include-gtidsa56fdfdc-7699-11ef-8f40-000c297f81d5:40 /data/binlog/mysql-bin.000003 > gtid.sql # --skip-gtids,使用mysqlbinlog截…...

华硕主板开启TPM 2.0
安装Windows11系统,需要打开TPM 安装 Windows 11 的方法 电脑健康状况检查应用验证最低系统要求 在电脑上启用 TPM 2.0 查看主板型号 winr msinfo32 查看 tpm 进入Advanced Mode(F7) 选择Security,进入Secure Boot,我…...

Linux 一键部署Mysql 8.0.37
mysql 前言 MySQL 是一个基于 SQL(Structured Query Language)的数据库系统,SQL 是一种用于访问和管理数据库的标准语言。MySQL 以其高性能、稳定性和易用性而闻名,它被广泛应用于各种场景,包括: Web 应用程序:许多动态网站和内容管理系统(如 WordPress)使用 MySQL 存…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...