rtmp协议分析(Message 消息,Chunk分块)

RTMP详细分析(三次握手)
librtmp分析(发送数据包处理)
librtmp分析(接收数据包处理)

目录

  • 1、Message(消息)
  • 2、Chunking(Message 分块)
    • 2.1、 Basic Header(基本的头信息)
      • 2.1.1、Basic Header为1个字节时
      • 2.1.2、Basic Header为2个字节时
      • 2.1.3、Basic Header为3个字节时
    • 2.2、Message Header(消息的头信息)
      • 2.2.1、Type(fmt) = 0:占用11个字节
      • 2.2.2、Type(fmt) = 1:占用7个字节
      • 2.2.3、Type(fmt) = 2:占用3个字节
      • 2.2.4、Type(fmt) = 3:占用0个字节
    • 2.3、Extended Timestamp(扩展时间戳)
    • 2.4、Chunk Data(块数据)
  • 3、协议控制消息(Protocol Control Message)
    • 3.1、Set Chunk Size(ID=1)
    • 3.2、Abort Message (ID=2)
    • 3.3、Acknowledgement (ID=3)、Window Acknowledgement Size (ID=5)
    • 3.4、Set Peer Bandwidth (ID=6)
  • 4、用户控制消息(User Control Message Events ID=4)
  • 5、命令消息(Command Message Message ID=17或20)
    • 5.1、NetConnection Commands(连接层的命令)
      • 5.1.1、connect:用于客户端向服务器发送连接请求
      • 5.1.2、Call:用于在对端执行某函数
      • 5.1.3、Create Stream:创建传递具体信息的通道
    • 5.2、NetStream Commands(流连接上的命令)
      • 5.2.1、play(播放)
      • 5.2.2、play2(播放)
      • 5.2.3、deleteStream(删除流)
      • 5.2.4、 receiveAudio(接收音频)
      • 5.2.5、 receiveVideo(接收视频)
      • 5.2.6、 publish(推送数据)
      • 5.2.7、 seek(定位流的位置)
      • 5.2.8、pause(暂停)
  • 6、数据消息(Data Message ID=15或18)(也是对应flv中的script data的tag type = 18 )
  • 7、共享消息(Shared Object Message ID=16或19)
  • 8、音频消息(Audio Message ID=8)(也是对应flv中的audio data的tag type = 8 )
  • 9、视频消息(Video Message ID=9)(也是对应flv中的video data的tag type = 9 )
  • 10、聚集消息(Aggregate Message ID=22)
  • 11、推流流程
  • 12、拉流流程

RTMP Chunk Stream
Chunk Stream是对传输RTMP Chunk的流的逻辑上的抽象,客户端和服务器之间有关RTMP的信息都在
这个流上通信。这个流上的操作也是我们关注RTMP协议的重点。

1、Message(消息)

这里的Message是指满足该协议格式的、可以切分成Chunk发送的消息,消息包含的字段如下:

Timestamp(时间戳):消息的时间戳(但不一定是当前时间),4个字节。

Length(长度):是指Message Payload(消息负载)即音视频等信息的数据的长度,3个字节。

TypeId(类型Id):消息的类型Id,1个字节。

Message Stream ID(消息的流ID,应该叫Chunk Stream ID更准确):每个消息的唯一标识,划分成Chunk和还原Chunk为Message的时候都是根据这个ID来辨识是否是同一个消息的Chunk的,4个字节,并且以小端格式存储。
(Message Stream ID如何产生?audio和video使用不同的Message Stream ID)

2、Chunking(Message 分块)

RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须
在一个Chunk发送完成之后才能开始发送下一个Chunk。每个Chunk中带有MessageID(Chunk Stream ID)代表属于哪个Message,接受端也会按照这个id来将chunk组装成Message。
为什么RTMP要将Message拆分成不同的Chunk呢?通过拆分,数据量较大的Message可以被拆分成较小的“Message”,这样就可以避免优先级低的消息持续发送阻塞优先级高的数据,比如在视频的传输过程中,会包括视频帧,音频帧和RTMP控制信息,如果持续发送音频数据或者控制数据的话可能就会造成视频帧的阻塞,然后就会造成看视频时最烦人的卡顿现象。同时对于数据量较大的Message,可以通过对
Chunk Header的字段来压缩信息,从而减少信息的传输量。
Chunk的默认大小是128字节,在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接受端会各自维护一个Chunk Size(srs流媒体服务器默认是60000),可以分别设置这个值来改变这一方发送的Chunk的最大值。大一点的Chunk减少了计算每个chunk的时间从而减少了CPU的占用率,但是它会占用更多的时间在发送上,尤其是在低带宽的网络情况下,很可能会阻塞后面更重要信息的传输。小一点的Chunk可以减少这种阻塞问题,但小的Chunk会引起过多额外的信息(Chunk中的Header),少量多次的传输也可能会造成发送的间断导致不能充分利用高带宽的优势,因此并不适合在高比特率的流中传输。在实际发送时应对要发送的数据用不同的Chunk Size去尝试,通过抓包分析等手段得出合适的Chunk大小,并且在传输过程中可以根据当前的带宽信息和实际信息的大小动态调Chunk的大小,从而尽量提高CPU的利用率并减少信息的阻塞机率。

Chunk Format(块格式):
在这里插入图片描述

2.1、 Basic Header(基本的头信息)

包含了chunk stream ID(流通道Id)和chunk type(chunk的类型),chunk stream id一般被简写为CSID,用来唯一标识一个特定的流通道,chunk type决定了后面Message Header的格式。Basic Header的长度可能是1,2,或3个字节,其中chunk type的长度是固定的(占2位,注意单位是位,bit),Basic Header是变长的,其长度取决于CSID的大小,在足够存储这两个字段的前提下最好用尽量少的字节从而减少由于引入Header增加的数据量。
RTMP协议最多支持65597个用户自定义chunk stream ID,范围为[3,65599] ,ID 0, 1, 2被协议规范直接使用,其中ID值为0, 1分表表示了Basic Header占用2个字节和3个字节:
ID值0:代表Basic Header占用2个字节,CSID在 [64,319] 之间;
ID值1:代表Basic Header占用3个字节,CSID在 [64,65599] 之间;
ID值2:代表该chunk是控制信息和一些命令信息,后面会有详细的介绍。

2.1.1、Basic Header为1个字节时

CSID占6位,6位最多可以表示64个数,因此这种情况下CSID在 [0,
63] 之间,其中用户可以定义的范围为 [3,63] 。
在这里插入图片描述

2.1.2、Basic Header为2个字节时

CSID占只占8位,第一个字节除chunk type占用的bit都置为0,第二
个字节用来表示CSID-64,8位可以表示 [0, 255] 共256个数,ID的计算方法为(第二个字节+64),范围为 [64,319]。
在这里插入图片描述

2.1.3、Basic Header为3个字节时

在此字段用3字节版本编码。ID的计算方法为(第三字节*256+第二字节+64)(Basic Header是采用小端存储的方式),范围为 [64,65599]。
在这里插入图片描述
可以看到2个字节和3个字节的Basic Header所能表示的CSID是有交集的 [64,319],但实际实现时还是应该秉着最少字节的原则使用2个字节的表示方式来表示 [64,319] 的CSID。

2.2、Message Header(消息的头信息)

包含了要发送的实际信息(可能是完整的,也可能是一部分)的描述信息。Message Header的格式和长度取决于Basic Header的chunk type,共有4种不同的格式,由上面所提到的Basic Header中的fmt 字段控制。其中第一种格式可以表示其他三种表示的所有数据,但由于其他三种格式是基于对之前chunk的差量化的表示,因此可以更简洁地表示相同的数据,实际使用的时候还是应该采样尽量少的字节表示相同意义的数据。

以下按照字节数从多到少的顺序分别介绍这4种格式的Message Header。

2.2.1、Type(fmt) = 0:占用11个字节

在这里插入图片描述
Type(fmg) = 0时,Message Header占用11个字节,其他三种能表示的数据它都能表示,但在chunk stream的开始的第一个chunk和头信息中的时间戳后退(即值与上一个chunk相比减小,通常在回退播放的时候会出现这种情况)的时候必须采用这种格式。

timestamp(时间戳):占用3个字节,因此它最多能表示到16777215=0xFFFFFF, 当它的值超过这个最大值时,这三个字节都置为1,这样实际的timestamp会转存到Extended Timestamp字段中,接受端在判断timestamp字段24个位都为1时就会去Extended timestamp中解析实际的时间戳。

message length(消息数据的长度):占用3个字节,表示实际发送的消息的数据如音频帧、视频帧等数据的长度,单位是字节。注意这里是Message的长度,也就是chunk属于的Message的总数据长度,而不是chunk本身Data的数据的长度。

message type id(消息的类型id):占用1个字节,表示实际发送的数据的类型,如8代表音频数据、9代表视频数据。

msg stream id(消息的流id):占用4个字节,表示该chunk所在的流的ID,和Basic Header的CSID一样,它采用小端存储的方式。

2.2.2、Type(fmt) = 1:占用7个字节

在这里插入图片描述
Type(fmg) = 1时,Message Header占用7个字节,省去了表示msg stream id的4个字节,表示此chunk和上一次发的chunk所在的流相同,如果在发送端只和对端有一个流链接的时候可以尽量去采取这种格式。

timestamp delta:占用3个字节,注意这个和type=0时不同,存储的是和上一个chunk的时间差。类似上面提到的timestamp,当它的值超过3个字节所能表示的最大值时,三个字节都置为1,实际的时间戳差值就会转存到Extended Timestamp字段中,接受端在判断timestamp delta字段24个位都为1时就会去Extended timestamp中解析时机的与上次时间戳的差值。

2.2.3、Type(fmt) = 2:占用3个字节

在这里插入图片描述
Type(fmg) = 2时,Message Header占用3个字节,相对于type=1格式又省去了表示消息长度的3个字节和表示消息类型的1个字节,表示此chunk和上一次发送的chunk所在的流、消息的长度和消息的类型都相同。余下的这三个字节表示timestamp delta,使用同type=1。

2.2.4、Type(fmt) = 3:占用0个字节

0字节!!!它表示这个chunk的Message Header和上一个是完全相同的,自然就不用再传输一遍了。当它跟在Type=0的chunk后面时,表示和前一个chunk的时间戳都是相同的。什么时候连时间戳都相同呢?就是一个Message拆分成了多个chunk,这个chunk和上一个chunk同属于一个Message。而当它跟在Type=1或者Type=2的chunk后面时,表示和前一个chunk的时间戳的差是相同的。比如第一个chunk的Type=0,timestamp=100,第二个chunk的Type=2,timestamp delta=20,表示时间戳为
100+20=120,第三个chunk的Type=3,表示timestamp delta=20,时间戳为120+20=140。

以上几种Type的Message Header对比:
在这里插入图片描述
在这里插入图片描述

2.3、Extended Timestamp(扩展时间戳)

在chunk中会有时间戳timestamp和时间戳差timestamp delta,并且它们不会同时存在,只有这两者之一大于3个字节能表示的最大数值0xFFFFFF=16777215时,才会用这个字段来表示真正的时间戳,否则这个字段为0。扩展时间戳占4个字节,能表示的最大数值就是0xFFFFFFFF=4294967295。当扩展时间戳启用时,timestamp字段或者timestamp delta要全置为0xFFFFFF,表示应该去扩展时间戳字段来提取真正的时间戳或者时间戳差。

2.4、Chunk Data(块数据)

用户层面上真正想要发送的与协议无关的数据,长度在(0,chunkSize]之间。

3、协议控制消息(Protocol Control Message)

在RTMP的chunk流会有一些特殊的值来代表协议的控制消息,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID可以为1,2,3,5,6,具体代表的消息会在下面依次说明。控制消息的接受端会忽略掉chunk中的时间戳,收到后立即生效。

3.1、Set Chunk Size(ID=1)

设置chunk中Data字段所能承载的最大字节数,默认为128B,通信过程中可以通过发送该消息来设置chunk Size的大小(不得小于128B),而且通信双方会各自维护一个chunkSize,两端的chunkSize是独立的。比如当A想向B发送一个200B的Message,但默认的chunkSize是128B,因此就要将该消息拆分为Data分别为128B和72B的两个chunk发送,如果此时先发送一个设置chunkSize为256B的消息,再发送Data为200B的chunk,本地不再划分
Message,B接受到Set Chunk Size的协议控制消息时会调整的接受chunk的Data的大小,也不用再将两个chunk组成为一个Message。在实际写代码的时候一般会把chunk size设置的很大,有的会设置为4096,FFMPEG推流的时候设置的是 60*1000,这样设置的好处是避免了频繁的拆包组包,占
用过多的CPU。

以下为代表Set Chunk Size消息的chunk的Data:
在这里插入图片描述
其中第一位必须为0,chunk Size占31个位,最大可代表2147483647=0x7FFFFFFF=2 -1,但实际上所有大于16777215=0xFFFFFF的值都用不上,因为chunk size不能大于Message的长度,表示Message的长度字段是用3个字节表示的,最大只能为0xFFFFFF。

3.2、Abort Message (ID=2)

当一个Message被切分为多个chunk,接受端只接收到了部分chunk时,发送该控制消息表示发送端不再传输同Message的chunk,接受端接收到这个消息后要丢弃这些不完整的chunk。Data数据中只需要一个CSID,表示丢弃该CSID的所有已接收到的chunk。
在这里插入图片描述

3.3、Acknowledgement (ID=3)、Window Acknowledgement Size (ID=5)

Window Acknowledgement Size用于设置窗口确认大小,Acknowledgement是窗口确认消息。
会话开始时,双方都要先对端发送Window Acknowledgement Size,用于指明期望获得确认的大小。当一端收到内容大小超过Window Acknowledgement Size,就要像对方发送Acknowledgement。
1、会话开始计算收到byte个数的时间点是收到Window Acknowledgement Size消息开始。
2、byte size不包括tcp包头,应该是chunk的大小,即从tcp 的recv函数中获得的内容大小。
3、双方都要向对方发送Window Acknowledgement Size和Acknowledgement。
4、发送端发送完Window Acknowledgement Size消息后,没有收到Acknowledgement是不再发送进行步的消息的——这样会容易引起错误,导致再也发送不出消息了。

Acknowledgement (ID=3):
客户端或者服务器在接收到等同于窗口大小的字节之后必须要发送给对端一个确认。窗
口大小是指发送者在没有收到接收者确认之前发送的最大数量的字节。这个消息定义了序列
号,也就是目前接收到的字节数。
在这里插入图片描述
Window Acknowledgement Size (ID=5):
‘客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发
送完窗口大小字节之后期望对端的确认。接收端在上次确认发送后接收到的指示数值后,或
者会话建立之后尚未发送确认,必须发送一个确认。
在这里插入图片描述
对于拉流端,一般在收到av_createStream后,接着play,然后发送Acknowledgement 以让服务器继续发送数据。

3.4、Set Peer Bandwidth (ID=6)

限制对端的输出带宽。接受端接收到该消息后会通过设置消息中的Window ACK Size来限制已发送但未接受到反馈的消息的大小来限制发送端的发送带宽。如果消息中的Window ACK Size与上一次发送给发送端的size不同的话要回馈一个Window Acknowledgement Size的控制消息。
在这里插入图片描述
1、Hard(Limit Type=0):接受端应该将Window Ack Size设置为消息中的值。

2、Soft(Limit Type=1):接受端可以讲Window Ack Size设为消息中的值,也可以保存原来的值(前提是原来的Size小与该控制消息中的Window Ack Size)。

3、Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit Type为0,本次也按Hard处理,否则忽略本消息,不去设置Window Ack Size。

4、用户控制消息(User Control Message Events ID=4)

(用户控制消息,Message Type ID=4):告知对方执行该信息中包含的用户控制事件,比如Stream Begin事件告知对方流信息开始传输。和前面提到的协议控制信息(Protocol Control Message)不同,这是在RTMP协议层的,而不是在RTMP chunk流协议层的,这个很容易弄混。该信息在chunk流中发送时,Message Stream ID=0,Chunk Stream
Id=2,Message Type Id=4。

支持以下用户控制事件类型:
在这里插入图片描述

5、命令消息(Command Message Message ID=17或20)

(命令消息,Message Type ID=17或20):表示在客户端盒服务器间传递的在对端执行某些操作的命令消息,如connect表示连接对端,对端如果同意连接的话会记录发送端信息并返回连接成功消息,publish表示开始向对方推流,接受端接到命令后准备好接受对端发送的流信息,后面会对比较常见的Command Message具体介绍。当信息使用AMF0编码时,Message Type ID=20,AMF3编码时Message Type ID=17。

发送端发送时会带有命令的名字,如connect,TransactionID表示此次命
令的标识,Command Object表示相关参数。接受端收到命令后,会返回以下三种消息中的一种:
_result消息表示接受该命令,对端可以继续往下执行流程。
_error消息代表拒绝该命令要执行的操作。
method name消息代表要在之前命令的发送端执行的函数名称。这三种回应的消息都要带有收到的命令消息中的TransactionId来表示本次的回应作用于哪个命令。
可以认为发送命令消息的对象有两种:
一种是NetConnection,表示双端的上层连接。
一种是NetStream,表示流信息的传输通道,控制流信息的状态,如Play播放流,Pause暂停。

5.1、NetConnection Commands(连接层的命令)

用来管理双端之间的连接状态,同时也提供了异步远程方法调用(RPC)在对端执行某方法,以下是常见的连接层的命令:

5.1.1、connect:用于客户端向服务器发送连接请求

握手之后先发送一个connect 命令消息,这些信息是以AMF格式发送的,消息的结构如下:
在这里插入图片描述
第三个字段中的Command Object中会涉及到很多键值对,使用时可以参考协议的官方文档。

消息的回应有两种,_result表示接受连接,_error表示连接失败。
以下是连接命令对象中使用的名称-值对的描述:
在这里插入图片描述
在这里插入图片描述
命令执行时消息流动如下:
1、 客户端发送 connect 命令到服务器端以请求对服务器端应用实例的连接。
2、 收到 connect 命令后,服务器端发送协议消息 ‘窗口确认大小’ 到客户端。服务器端也会连接到 connect 命令中提到的应用。
3、 服务器端发送协议消息 ‘设置对端带宽’ 到客户端。
4、在处理完协议消息 ‘设置对端带宽’ 之后客户端发送协议消息 '窗口确认大小’到服务器端。
5、 服务器端发送另一个用户控制消息 (StreamBegin) 类型的协议消息到客户端。
6、 服务器端发送结果命令消息告知客户端连接状态 (success/fail)。这一命令定义了事务ID(常常为 connect 命令设置为 1)。这一消息也定义了一些属性,比如 FMS 服务器版本 (字符串)。之外,它还定义了݊其他连接关联到的信息,比如 level (字符串)、code (字符串)、 description (字符串)、objectencoding (数字) 等等。

5.1.2、Call:用于在对端执行某函数

即常说的RPC:远程进程调用,消息的结构如下:
在这里插入图片描述
如果消息中的TransactionID不为0的话,对端需要对该命令做出响应,响应的消息结构如下:
在这里插入图片描述

5.1.3、Create Stream:创建传递具体信息的通道

从而可以在这个流中传递具体信息,传输信息单元为Chunk。
当发送完createStream消息之后,解析服务器返回的消息会得到一个stream ID, 这个ID也就是以后和服务器通信的 message stream ID, 一般返回的是1,不固定。
在这里插入图片描述

5.2、NetStream Commands(流连接上的命令)

Netstream建立在NetConnection之上,通过NetConnection的createStream命令创建,用于传输具体的音频、视频等信息。在传输层协议之上只能连接一个NetConnection,但一个NetConnection可以建立多个NetStream来建立不同的流通道传输数据。

以下会列出一些常用的NetStream Commands,服务端收到命令后会通过onStatus的命令来响应客户端,表示当前NetStream的状态。

onStatus命令的消息结构如下:
在这里插入图片描述

5.2.1、play(播放)

由客户端向服务器发起请求从服务器端接受数据(如果传输的信息是视频的话就是请求开始播流),可以多次调用,这样本地就会形成一组数据流的接收者(创建一个播放列表)。注意其中有一个reset字段,表示是覆盖之前的播流(设为true)还是重新开始一路播放(设为false)。
play命令的结构如下:
在这里插入图片描述
在这里插入图片描述
命令执行时的消息流动:
1、 当客户端从服务器端接收到 createStream 命令的结果是为 success 时,发送play 命令。
2、 一旦接收到 play 命令,服务器端发送一个协议消息来设置块大小。
3、 服务器端发送另一个协议消息( 用户控制 ) , 这个消息中定义了
‘StreamIsRecorded’ 事件和流 ID。消息在前两个字节中保存事件类型,在后四个字节中保存流 ID。
4、服务器端发送另一个协议消息 (用户控制),这一消息包含 ‘StreamBegin’ 事件,来指示发送给客户端的流的起点。
5、 如果客户端发送的 play 命令成功,服务器端发送一个 onStatus 命令消息NetStream.Play.Start & NetStream.Play.Reset。只有当客户端发送的 play 命令设置了 reset 时,服务器端才会发送 NetStream.Play.Reset。如果要播放的流没有找到,服务器端发送 onStatus消息 NetStream.Play.StreamNotFound。
之后,服务器端发送视频和音频数据,客户端对݊其进行播放。

5.2.2、play2(播放)

和上面的play命令不同的是,play2命令可以在不改变播放内容时间轴的情况下切换到不同的比特率,服务器端会维护多种比特率的文件来供客户端使用play2命令来切换。
在这里插入图片描述
在这里插入图片描述

5.2.3、deleteStream(删除流)

用于客户端告知服务器端本地的某个流对象已被删除,不需要再传输此
路流。
在这里插入图片描述

5.2.4、 receiveAudio(接收音频)

NetStream 通过发送 receiveAudio 消息来通知服务器端是否发送音频到客户端。
receiveAudio命令结构如下:
在这里插入图片描述
如果发送来的 receiveAudio 命令布尔字段被设为 false 时服务器端不发送任何回复。
如果这一标识被设为 true, 服务器端发送状态消息NetStream.Seek.Notify 和
NetStream.Play.Start 进行回复。

5.2.5、 receiveVideo(接收视频)

NetStream 通过发送receiveVideo 消息来通知服务器端是否发送视频到客户端。
receiveVideo命令结构如下:
在这里插入图片描述
如果发送来的 receiveVideo 命令布尔字段被设为 false 时服务器端不发送任何回复。
如果这一标识被设为 true, 服务器端发送状态消息NetStream.Seek.Notify 和
NetStream.Play.Start 进行回复。

5.2.6、 publish(推送数据)

由客户端向服务器发起请求推流到服务器。
publish命令结构如下:
在这里插入图片描述

5.2.7、 seek(定位流的位置)

定位到视频或音频的某个位置,以毫秒为单位。
seek命令的结构如下:
在这里插入图片描述

5.2.8、pause(暂停)

客户端告知服务端停止或恢复播放。
pause命令的结构如下:
在这里插入图片描述
如果Pause为true即表示客户端请求暂停的话,服务端暂停对应的流会返回NetStream.Pause.Notify的onStatus命令来告知客户端当前流处于暂停的状态,当Pause为false时,服务端会返回NetStream.Unpause.Notify的命令来告知客户端当前流恢复。如果服务端对该命令响应失败,返回_error信息。

6、数据消息(Data Message ID=15或18)(也是对应flv中的script data的tag type = 18 )

(数据消息,Message Type ID=15或18):传递一些元数据(MetaData,比如视频名,分辨率等等)或者用户自定义的一些消息。当信息使用AMF0编码时,Message Type ID=18,AMF3编码时Message Type ID=15。

7、共享消息(Shared Object Message ID=16或19)

(共享消息,Message Type ID=16或19):表示一个Flash类型的对象,由键值对的集合组成,用于多客户端,多实例时使用。当信息使用AMF0编码时,Message Type ID=19,AMF3编码时Message Type ID=16。
每个消息可以包含有不同事件。
在这里插入图片描述
支持以下事件:
在这里插入图片描述

8、音频消息(Audio Message ID=8)(也是对应flv中的audio data的tag type = 8 )

(音频信息,Message Type ID=8):音频数据。

9、视频消息(Video Message ID=9)(也是对应flv中的video data的tag type = 9 )

(视频信息,Message Type ID=9):视频数据。

10、聚集消息(Aggregate Message ID=22)

(聚集信息,Message Type ID=22):多个RTMP子消息的集合。

11、推流流程

在这里插入图片描述

在这里插入图片描述

12、拉流流程

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378971.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将Sharepoint Server 2010部署到WINDOWS 7

首先祝CNBLOGS上的筒子们新年快乐。Sharepoint 2010 BETA版发布已经有段时间了,总是感觉MS的步伐要比我们这些追逐他的人快很多,不过确实他的每一次革新总给我们带来了惊喜。 前几天报名参加了SHAREPOINT 2010 DAY 活动(详情),等待着1月16日体…

嵌入式实训-day1

完全复制一个文件的内容到另外一个文件 思路解析: 首先我这里使用了三个.c文件,分别是:yanyu.c、yanyu_old.c、yanyu_now.c 其中yanyu.c负责将yanyu_old.c中的内容读入到buff缓冲区中,然后再从buff缓冲区中将数据写入到yanyu_no…

计量经济学建模_浅谈统计学模型(兼计量经济学模型)

计量经济学模型是从统计学模型中衍生出来的,故将它们一并放在此处进行说明。实际上,很多人在很久之前就督促我写一篇统计学和计量经济学模型的文章,但我太懒惰,一直拖到现在,也是十分汗颜。先讲一些统计学上的基础故事…

linux文件存储、inode、硬链接、软链接

目录介绍inode的内容inode的大小inode号码目录文件硬链接软链接介绍 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会…

工具栏的打印图标不见了_显示屏下方的工具栏里的小图标不见了怎么弄回来

优质回答 回答者:昂天大笑出门去一般右下角那些小图标都是与系统同步启动的一些功能,有的是没必要让它们同步启动的,启动的东西越多,系统反应就越慢。你说的工具栏里的小图标没了,可能是那些工具不能和系统同步启动了&…

使用sshfs挂载linux远程服务器目录到windows

目录下载winfsp 和 sshfs-win使用方式1、添加连接2、连接3、其他操作修改:删除:下载winfsp 和 sshfs-win 前往 github 对应项目的 release 中下载最新版本: sshfs-win:https://github.com/billziss-gh/sshfs-win/releases winfs…

法学学士学位的完整形式是什么?

LL.B:拉丁文的Legum Baccalaureus(俗称法学学士) (LL.B: Legum Baccalaureus in Latin (commonly known as Bachelor of Laws)) LL.B is an abbreviation of Legum Baccalaureus in Latin which is generally known as Bachelor of Laws. It is a bachelors degree …

Qt创建工程及导入资源图片

一、打开软件 二、 三、 四、 五、 六、 七、 导入其他文件资源,比如图片资源: 一、 二、 三、 四、 五、 六、导入图片 七、 八、 该图片导入项目里面了

protect 继承_(转)public、protect、private继承方式 C++

C里面的结构体内的成员不受任何机制保护,直接能用,比如StructtypeA.x;StructtypeA.y;StructtypeA.fun();而c则不同,c的类像强化型的结构体public公有继承protected保护继承private私有继承我们知道类的private和protected成员,在类外是不可以…

原型模型| 软件工程

A prototype means a preliminary model of anything which gives us a rough idea about the basic functionalities that the real model would have. The prototyping model follows the same strategy. 原型意味着任何事物的初步模型,这使我们对真实模型具有的基…

librtmp分析(发送数据包处理)

RTMP详细分析(三次握手) RTMP详细分析(Message 消息,Chunk分块) librtmp分析(接收数据包处理) rtmp协议中的message的发送涉及有message 分chunk、base header长度的变化、message header长度的变化,只查看…

tomcat没有错,但是还是一闪而过(端口被占用)

首先&#xff0c;看tomcat日志文件&#xff0c;在tomcat目录下有个logs文件夹&#xff0c;进去找到刚才运行时的日志文件。 然后&#xff0c;我的是Address already in use: JVM_Bind <null>:8080&#xff0c;8080端口被占用了。 此时需要将该端口给kill掉 cmd netsta…

librtmp分析(接收数据包处理)

RTMP详细分析&#xff08;三次握手&#xff09; RTMP详细分析(Message 消息&#xff0c;Chunk分块) librtmp分析&#xff08;发送数据包处理&#xff09; rtmp协议中的message的接收涉及有message 组合多个chunk、相对时间戳计算绝对值。 分析一下librtmp库中的int RTMP_ReadP…

动态可缓存的内容管理系统(CMS)(转)

摘要&#xff1a;内容管理系统(CMS)在各大商业站点和门户站点中扮演着重要的角色&#xff0c;是内容有效组织和快速发布极为重要的基础平台。目前主流的内容发布系统都使用静态页面进行内容发布&#xff0c;在我们的实际使用过程中我们深切的感受到静态内容发布存在着很多弊端&…

mysql 自动化 安装_mysql自动化安装

MySQL安装一般使用RPM或者源码安装的方式。RPM安装的优点是快速,方便.缺点是不能自定义安装目录.如果需要调整数据文件和日志文件的存放位置,还需要进行一些手动调整。源码安装的优点是可以自定义安装目录,缺点是编译时间长,过程复杂其实还有一种方式,定制RPM包.它相当于用源码…

hls协议分析

目录1、简介1.1、 综述1.2 、HLS 协议编码格式要求1.3 、HLS 协议优势1.4 、HLS 协议劣势1.5 、框架图2、m3u8文件2.1 、单码率适配流m3u8文件2.2 、多码率适配流m3u8文件2.3 、Playlist file2.4 、Tags3、ts文件3.1 、ts文件结构3.2、ts文件结构部分截图3.3、ts层&#xff08;…

OpenGL 学习笔记(1)初始化窗体

前言 学习OpenGL只是兴趣爱好&#xff0c;因为对图形比较感兴趣.将以OpenGl的红宝书(7)和蓝宝石书(4)为基础,虽然手头有红宝书书&#xff0c;但感觉没蓝宝石书写的好 准备工作 首先要下载一个工具库(GLUT) http://www.opengl.org/resources/libraries/glut/ 只要把相应文件放在…

基于云平台的家居综合监测管理系统的设计与实现

时间过得飞快&#xff0c;转眼间大四即将毕业&#xff0c;有点留恋和不舍。可能是越是到了离别的时候&#xff0c;越开始珍惜吧。大一开始&#xff0c;通过考核进入了学校院系实验室开始学习&#xff0c;这期间自学了很多东西&#xff0c;很充实&#xff0c;也参加过很多比赛&a…

小白学数据分析-----留存率分析_I[次日留存率突然下降了50%?]

最近在做留存分析时&#xff0c;遇到了不少的情况&#xff0c;也经常会有人问我&#xff0c;为什么我的游戏突然次日留存率降了一半。如果留存率是单单作为一个简单的指标的话&#xff0c;那对你价值还是蛮有限的&#xff0c;今天就和大家说说一个case&#xff0c;这是不久前解…

mysql映射mapper_SQL映射器Mapper接口(MyBatis)

SQL映射器Mapper接口MyBatis基于代理机制&#xff0c;可以让我们无需再写Dao的实现。直接把以前的dao接口定义成符合规则的Mapper。注意事项&#xff1a;1&#xff0e;接口必须以Mapper结尾,名字是DomainMapper2&#xff0e;mapper.xml文件要和Mapper接口建立关系,通过namespac…