RTP 提供带有 实时特性的端对端数据传输服务,传输的数据如:交互式的音频和视频。那些服务包括有效载荷类型定义、序列号、时间戳和传输监测控制。RTP支持数据使用多播分发机制转发到多个目的地。
注意 RTP 本身没有提供任何的机制来确保实时的传输或其他的服务质量保证,而是由低层的服务来完成。它不保证传输或防止乱序传输,它不假定下层网络是否可靠,是否按顺序传送数据包。RTP 包含的序列号允许接受方重构发送方的数据包顺序,但序列号也用来确定一个数据包的正确位置。
RTP用于在单播或多播网络中传送实时数据。它们典型的应用场合如下:
简单的多播音频会议
音频和视频会议
翻译器和混合器
流媒体是指Internet上使用流式传输技术的连续时基媒体。当前在Internet上传输音频和视频等信息主要有两种方式:
文件下载
下载情况下,用户需要先下载整个媒体文件到本地,然后才能播放媒体文件。在视频直播等应用场合,由于生成整个媒体文件要等直播结束,也就是用户至少要在直播结束后才能看到直播节目,所以用下载方式不能实现直播。
流式传输
流式传输是实现流媒体的关键技术,使用流式传输可以边下载边观看流媒体节目。
由于Internet是基于分组传输的,所以接收端收到的数据包往往有延迟和乱序(流式传输构建在UDP上)。要实现流式传输,就是要从降低延迟和恢复数据包时序入手。
使用接收缓冲,可以将接收到的数据包缓存起来,然后根据数据包的封装信息(如包序号和时戳等),将乱序的包重新排序,最后将重新排序了的数据包放入播放缓冲播放。
为什么需要播放缓冲呢?容易想到,由于网络不可能很理想,并且对数据包排序需要处理时耗,我们得到排序好的数据包的时间间隔是不等的。如果不用播放缓冲,那么播放节目会很卡,这叫时延抖动(Jitter)。相反,使用播放缓冲,在开始播放时,花费几十秒钟先将播放缓冲填满(例如PPLIVE),可以有效地消除时延抖动,从而在不太损失实时性的前提下实现流媒体的顺畅播放。
RTP(实时传输协议),顾名思义,它是用来提供实时传输的,因而可以看作是传输层的一个子层,下图给出了流媒体应用中的协议体系结构。
RTP被划分在传输层,它建立在UDP之上。同UDP协议一样,为了实现其传输功能,RTP也有固定的封装格式。UDP本身就是一种不可靠协议,所以建立在UDP之上的RTP就需要一个RTCP来提高一定的可靠性。

也有人把RTP归为应用层的一部分,这是从应用开发者的角度来说的。
操作系统中TCP/IP等协议所提供的是我们最常用的服务,而RTP的实现还是要考开发者自己。因此,从开发的角度来说,RTP的实现和应用层协议的实现没什么不同,所以将RTP看成应用层协议。
RTP实现者在发送RTP数据时,需先将数据封装成RTP包,而在接收到RTP数据包,需要将数据从RTP包中提取出来。
一个协议的封装时为了满足协议的功能需求,从前面提出的功能需求,可以推测RTP封装中应该有同步源和时间戳等信息。完整的RTP格式如下所示:

由上图中可知道RTP报文由两个部分构成:RTP报头 和 RTP有效负载,RTP头固定区一共12个字节。
| 版本号(V) | 2B | RTP的版本号 |
| 填充标志(P) | 1B | 净荷末端是否包含一个或多个填充字节,净荷长度必须是4字节的倍数,因此需要填充字节使得净荷长度满足4字节倍数的要求,净荷的最后一个字节给出填充字节数 |
| 扩展位(X) | 1B | RTP 首部扩展,RTP 首部扩展是可选的,若扩展位置1,表明在固定首部之后紧跟着一个首部扩展 |
| 提供源计数器(CC) | 4B | RTP 首部中提供源标识符列表中的项数 |
| 标记(M) | 1B | 含义取决于携带净荷的类型,对于视频,标记一帧的结束;对于音频,标记会话的开始 |
| 有效载荷类型(PT) | 7B | RTP 净荷的格式,通常,单个 RTP 分组所包含的净荷只能用一种净荷格式对多媒体数据进行编码 |
| 序列号(sequence number) | 16B | 会话开始时,发送端生成随机数作为初始值,传送 RTP 分组时,每传送一组该字段增 1,在会话存在期间,顺序发送的一串 RTP 分组的序号是递增的,接收端可以根据序号检测是否存在分组丢失或错序 |
| 时间戳(timestamp) | 32B | 净荷中第一个采样数据的采样时间,每一个采样数据的采样时间通过一个单调且线性递增的时钟获得,时钟频率取决于净荷数据的编码格式,相邻两个 RTP 分组的时间戳差值,取决于前一个 RTP 分组净荷中包含的采样数据数目 |
| 同步源标识符(SSRC) | 32B | 标明同步源,同步源是一个负责发送 RTP 分组并在 RTP 分组中设置序号和时间戳的实体,标识符是会话中全局惟一的,若 RTP 分组来自混合器则同步源标识符给出的是混合器的标识符 |
| 提供源标识符列表(CSRC) | (0~15)*32B | 最多允许存在16个提供源标识符,若 RTP 分组来自混合器则提供源标识符列表给出进入混合器的各个信号的信号源标识符
|
下图为某网关上抓取的RTP报文:

如果P==1,那么就会在载荷末尾添加一个字节的填充位,组包的时候加上他,解包的时候跳过他
int rtp_packet_des(struct rtp_packet_t *pkt, const void* data, int bytes) {
// ......
// padding
if (1 == pkt->rtp.p)
{
uint8_t padding = ptr[bytes - 1];
if (pkt->payloadlen < padding) {
assert(0);
return -1;
} else {
pkt->payloadlen -= padding;
}
}
// ......
}

extended_seq_num = seq_num + (65536 * wrap_around_count)
uint16_t udelta = seq – max_seq
if (udelta < max_dropout) {
if (seq < max_seq) {
wrap_around_count++
}
max_seq = seq;
} else if (udelta <= 65535 – max_misorder) {
// The sequence number made a very large jump
if (seq == bad_seq) {
// Two sequential packets received; assume the
// other side has restarted without telling us
...
} else {
bad_seq = seq + 1;
}
} else {
// Duplicate or misordered packet
...
}
- 用于生成时间戳的媒体时钟的标称频率,由使用中的配置文件或有效负载格式定义
- 对于具有静态有效负载类型分配的有效负载格式,使用静态有效负载类型时时钟频率是隐式的(指定为有效负载类型分配的一部分)
- 对于具有动态有效负载类型分配的有效负载格式,分配过程必须指定频率以及有效负载类型,选择的速率必须满足所需的精度执行音视频同步并度量网络传输时间的变化
- 时钟频率可能不会任意选择,大多数有效负载格式定义一个或多个可接受的速率
#define RTP_VERSION 2 // RTP version field must equal 2 (p66)
// RTP头固定12个字节
typedef struct
{
uint32_t v:2; /* protocol version */
uint32_t p:1; /* padding flag */
uint32_t x:1; /* header extension flag */
uint32_t cc:4; /* CSRC count */
uint32_t m:1; /* marker bit */
uint32_t pt:7; /* payload type */
uint32_t seq:16; /* sequence number */
uint32_t timestamp; /* timestamp */
uint32_t ssrc; /* synchronization source */
} rtp_hdr_t;
// RTP头解析宏定义
#define RTP_V(v) ((v >> 30) & 0x03) /* protocol version */
#define RTP_P(v) ((v >> 29) & 0x01) /* padding flag */
#define RTP_X(v) ((v >> 28) & 0x01) /* header extension flag */
#define RTP_CC(v) ((v >> 24) & 0x0F) /* CSRC count */
#define RTP_M(v) ((v >> 23) & 0x01) /* marker bit */
#define RTP_PT(v) ((v >> 16) & 0x7F) /* payload type */
#define RTP_SEQ(v) ((v >> 00) & 0xFFFF) /* sequence number */
#endif /* !_rtp_header_h_ */

其中X位如果为1,就表示CSRC后面还有一些额外的RTP扩展头,其格式如下:

但是这种形式只能够附加一个扩展头,为了支持多个扩展头,RFC5285以 defined by profile 进行了扩展。
One-Byte
RTP头后的第一个16位固定为 0XBEDE 标志,意味着这是一个one-byte扩展,length = 3 说明后面有三个扩展头,每个扩展头首先以一个byte开始,前4位是这个扩展头的ID, 后四位是data的长度-1,譬如说L=0意味着后面有1个byte的data,同理第二个扩展头的L=1说明后面还有2个byte的data,但是注意,其后没有紧跟第三个扩展头,而是添加了2个byte大小的全0的data,这是为了作填充对齐,因为扩展头是以为32bit作填充对齐的。

Two-Byte
RTP头后可以看到16位固定为 0x100 + 0x0, 接下来的为 length=3 表示接下来有3个头,接下来的就是扩展头和数据,扩展头除了ID和L相对于one-byte header从4bits变成了8bits之后,其余都一样。

RTP Packet = RTP Header + RTP Payload。
RTP Payload结构一般分为3种:
各种RTP分组在RTP Header后面跟着 F | NRI | Type 结构的NALU Header来判断分组类型。

- 0:保留
- 1-23:H264编码规定的数据类型,单NALU分组直接使用此值
- 24-27:聚合分组类型(聚合分组一般使用24 STAP-A)
- 28-29:分片分组类型(分片分组一般使用28FU-A)
- 30-31:保留

单NALU(single nalu)
此结构的NALU Header结构可以直接使用原始码流NALU Header,所以单NALU分组 Type = 1~23。封装RTP包的时候可以直接把查询到的NALU去掉起始码(startcode)后的部分当作单NALU分组的RTP包Payload部分。
rtp_packet = rtp_header + nalu_header +
(nalu_data)

单时聚合分组(single-time aggregation)
通常采用STAP-A (Type=24)结构封装RTP聚合分组,下图为包含2个NALU的采用STAP-A结构的聚合分组。
rtp_packet = rtp_header + stap_a_nalu_header +
(nalu_size + nalu_header + nalu_data) +
(nalu_size + nalu_header + nalu_data)
STAP-A

STAP-B

// 单时聚合分组, 这里假设已经做完了排序
static int rtp_h264_unpack_stap(struct rtp_decode_h264_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp, int stap_b)
{
int r, n;
uint16_t len;
uint16_t don;
r = 0;
// prt: 去除RTP包头的数据包
// bytes: 数据包字节数
// unpacker->ptr: 数据缓存
// unpacker->size: 缓存字节数
// STAP-A: STAP-A HDR(1) + (NALU SIZE(2) + NALU HDR(1) + NALU Data(size)) * n
// STAP-B: STAP-A HDR(1) + DON(2) + (NALU SIZE(2) + NALU HDR(1) + NALU Data(size)) * n
n = stap_b ? 3 : 1;
if (bytes < n) {
assert(0);
return -EINVAL; // error
}
// DON,这里也就做做样子
don = stap_b ? nbo_r16(ptr + 1) : 0;
// 数据区
ptr += n;
// NALU循环
for(bytes -= n; 0 == r && bytes > 2; bytes -= len + 2)
{
// NALU Size
len = nbo_r16(ptr);
if(len + 2 > bytes) {
assert(0);
unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
unpacker->size = 0;
return -EINVAL; // error
}
#if 0
assert(H264_NAL(ptr[2]) > 0 && H264_NAL(ptr[2]) < 24);
#endif
// ptr+2: NALU HDR + NALU Data
r = unpacker->handler.packet(unpacker->cbparam, ptr + 2, len, timestamp, unpacker->flags);
unpacker->flags = 0;
unpacker->size = 0;
// next NALU, 2表示NALU Size, len表示NALU HDR + NALU Data
ptr += len + 2;
// (don+1)%65536的意思是, 2^16的回绕
don = (don + 1) % 65536;
}
return 0 == r ? 1 : r;
}
多时聚合分组(multi-time aggregation)
MTAP-16

MTAP-24

分片分组(Fragmentation Unit)
通常采用无DON字段的 FU-A 结构封装 RTP 分片分组。各种 RTP 分组在 RTP Header 后面都跟着 NALU Header 结构,来判定分组类型。

- S=1 表示这个RTP包为分片分组第一个分片
- E=1 表示为分片分组最后一个分片
- 除了首尾分片,中间的分片S&E都设为0,R为保留位,设为0;

rtp_packet = rtp_header + fu_a_nalu_header +
(fu_header + fu_payload)
FU-A

FU-B

// 5.8. Fragmentation Units (FUs) (p29)
// FU-A,FU-B:切片分组, 这里假设已经做完排序
static int rtp_h264_unpack_fu(struct rtp_decode_h264_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp, int fu_b)
{
int r, n;
uint8_t fu_header;
r = 0;
// prt: 去除RTP包头的数据包
// bytes: 数据包字节数
// unpacker->ptr: 数据缓存
// unpacker->size: 缓存字节数
// FU-A: FU INDI(1) + FU HDR(1) + FU Payload
// FU-B: FU INDI(1) + FU HDR(1) + DON(2) + FU Payload
n = fu_b ? 4 : 2;
if (bytes < n || unpacker->size + bytes - n > RTP_PAYLOAD_MAX_SIZE) {
assert(0);
return -EINVAL; // error
}
// NALU总大小 > 解包容量
if (unpacker->size + bytes - n + 1 > unpacker->capacity)
{
void* p = NULL;
int size = unpacker->size + bytes + 1;
size += size / 4 > 128000 ? size / 4 : 128000;
p = realloc(unpacker->ptr, size);
if (!p) {
// set packet lost flag
unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
unpacker->size = 0;
return -ENOMEM; // error
}
unpacker->ptr = (uint8_t*)p;
unpacker->capacity = size;
}
// FU-HDR 第二个字节
fu_header = ptr[1];
// 首切包
if (FU_START(fu_header)) {
#if 0
if (unpacker->size > 0)
{
unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_CORRUPT;
unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, unpacker->timestamp, unpacker->flags);
unpacker->flags = 0;
unpacker->size = 0;
}
#endif
// NALU Type
unpacker->size = 1;
// 清空缓存
unpacker->ptr[0] = (ptr[0]/*indicator*/ & 0xE0) | (fu_header & 0x1F);
#if DEBUG
assert(H264_NAL(unpacker->ptr[0]) > 0 && H264_NAL(unpacker->ptr[0]) < 24);
#endif
} else {
if (0 == unpacker->size) {
unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
return 0; // packet discard
}
#if DEBUG
assert(unpacker->size > 0);
#endif
}
// 时间戳,理论上应该是一样的
unpacker->timestamp = timestamp;
// 缓存追加
// prt+n: 去除FuIndicator,FuHeader,DON
if (bytes > n) {
#if DEBUG
assert(unpacker->capacity >= unpacker->size + bytes - n);
#endif
memmove(unpacker->ptr + unpacker->size, ptr + n, bytes - n);
unpacker->size += bytes - n;
}
// 尾切包
if(FU_END(fu_header))
{
// 多次传入数据后等到FU_END的时候得到一个完整的NALU
if(unpacker->size > 0) {
r = unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, timestamp, unpacker->flags);
}
unpacker->flags = 0;
unpacker->size = 0; // reset
}
return 0 == r ? 1 : r; // packet handled
}

打包步骤:
通常根据AAC码率大小可以分为 Low Bit-rate AAC 以及 High Bit-rate AAC 模式。
Low Bit-rate AAC
在Low Bit-rate AAC下规定的一帧大小最大不超过63字节

High Bit-rate AAC
High Bit-rate AAC下规定一帧大小最大不超过8192字节

一个RTP包中可以有一个 AU-headers-length 和 N 个 AU Header 和 N 个AU(AU是每包实际音频数据流)

AU-headers-length
头两个字节表示au-header的长度,单位是bit。一个AU-header长度是两个字节(16bit)因为可以有多个au-header,所以AU-headers-length的值是 16 的倍数,一般音频都是单个音频数据流的发送,所以AU-headers-length的值是16。
因为单位是bit,除以8就是auHeader的字节长度;又因为单个auheader字节长度2字节,所以再除以2就是auheader的个数。(注意:AU-header长度并不是固定为2字节,具体要看SDP)
AU-header

AU
AU就是去掉ADTS Header的AAC-ES。

RTP视频H.264和音频AAC的时间轴:

单时聚合分组

分片分组



// RTP解包
nt rtp_packet_deserialize(struct rtp_packet_t *pkt, const void* data, int bytes)
{
uint32_t i, v;
int hdrlen;
const uint8_t *ptr;
// RFC3550 5.1 RTP Fixed Header Fields(p12)
if (bytes < RTP_FIXED_HEADER)
return -1;
ptr = (const unsigned char *)data;
memset(pkt, 0, sizeof(struct rtp_packet_t));
// pkt header
v = nbo_r32(ptr);
pkt->rtp.v = RTP_V(v);
pkt->rtp.p = RTP_P(v);
pkt->rtp.x = RTP_X(v);
pkt->rtp.cc = RTP_CC(v);
pkt->rtp.m = RTP_M(v);
pkt->rtp.pt = RTP_PT(v);
pkt->rtp.seq = RTP_SEQ(v);
pkt->rtp.timestamp = nbo_r32(ptr + 4);
pkt->rtp.ssrc = nbo_r32(ptr + 8);
#if DEBUG
assert(RTP_VERSION == pkt->rtp.v);
#endif
// rtp.cc表示CSRC个数
// 假如rtp.cc > 0, 头长度需要带上CSRC的字节数
hdrlen = RTP_FIXED_HEADER + pkt->rtp.cc * 4;
if (RTP_VERSION != pkt->rtp.v || bytes < hdrlen + (pkt->rtp.x ? 4 : 0) + (pkt->rtp.p ? 1 : 0))
return -1;
// 保存SCRC
for (i = 0; i < pkt->rtp.cc; i++) {
pkt->csrc[i] = nbo_r32(ptr + 12 + i * 4);
}
// 获取Payload
assert(bytes >= hdrlen);
pkt->payload = (uint8_t*)ptr + hdrlen; // 跳过头部 拿到payload
pkt->payloadlen = bytes - hdrlen; // payload长度
// RTP Header Extension
// 如果有扩展, rtp.payload需要偏移
if (1 == pkt->rtp.x)
{
const uint8_t *rtpext = ptr + hdrlen;
assert(pkt->payloadlen >= 4);
pkt->extension = rtpext + 4;
pkt->reserved = nbo_r16(rtpext);
pkt->extlen = nbo_r16(rtpext + 2) * 4;
if (pkt->extlen + 4 > pkt->payloadlen) {
assert(0);
return -1;
} else {
pkt->payload = rtpext + pkt->extlen + 4;
pkt->payloadlen -= pkt->extlen + 4;
}
}
// RTP Header Padding
if (1 == pkt->rtp.p)
{
uint8_t padding = ptr[bytes - 1];
if (pkt->payloadlen < padding) {
assert(0);
return -1;
} else {
pkt->payloadlen -= padding;
}
}
return 0;
}
// RTP封包
int rtp_packet_serialize(const struct rtp_packet_t *pkt, void* data, int bytes)
{
int hdrlen;
hdrlen = rtp_packet_serialize_header(pkt, data, bytes);
if (hdrlen < RTP_FIXED_HEADER || hdrlen + pkt->payloadlen > bytes)
return -1;
memcpy(((uint8_t*)data) + hdrlen, pkt->payload, pkt->payloadlen);
return hdrlen + pkt->payloadlen;
}
// RTP头打包
int rtp_packet_serialize_header(const struct rtp_packet_t *pkt, void* data, int bytes)
{
int hdrlen;
uint32_t i;
uint8_t* ptr;
if (RTP_VERSION != pkt->rtp.v || 0 != (pkt->extlen % 4)) {
assert(0); // RTP version field must equal 2 (p66)
return -1;
}
// RFC3550 5.1 RTP Fixed Header Fields(p12)
// 对于CC值计算CSRC的长度扩展
// 对于X扩展计算Padding的长度扩展
hdrlen = RTP_FIXED_HEADER + pkt->rtp.cc * 4 + (pkt->rtp.x ? 4 : 0);
if (bytes < hdrlen + pkt->extlen)
return -1;
ptr = (uint8_t *)data;
nbo_write_rtp_header(ptr, &pkt->rtp);
ptr += RTP_FIXED_HEADER;
// CSRC列表封装到头部
for (i = 0; i < pkt->rtp.cc; i++, ptr += 4) {
nbo_w32(ptr, pkt->csrc[i]);
}
// RTP Header Extension
if (1 == pkt->rtp.x)
{
// 5.3.1 RTP Header Extension
assert(0 == (pkt->extlen % 4));
nbo_w16(ptr, pkt->reserved);
nbo_w16(ptr + 2, pkt->extlen / 4);
memcpy(ptr + 4, pkt->extension, pkt->extlen); // extension封装到头部
ptr += pkt->extlen + 4;
}
return hdrlen + pkt->extlen;
}
其实做自媒体的成本并不高,入门只需要一部手机即可!在手机上找视频素材、使用手机剪辑视频、最后使用手机发布视频作品获得收益!方法并不难,今天这期内容就来给粉丝们分享一种小方法,每天稳定收益100-300,抓紧点赞收藏!1、找素材(1)使用手机拍摄自己喜欢的经典段落,使用程序把文案内容提取出来(2)也可以在豆瓣、知乎、微博等网站中找一些自己需要的文案素材(3)把文案进行润色修改,可以加入一些自己的观点(4)视频素材可以使用软件中自带的素材,也可以在素材网站中下载完整版的素材2、文案配音(1)把复制好的文案直接导入小程序中(2)调整音色、音调后一键合成音频即可(3)可以选择自己朗读配音,需要花一点时
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
尝试通过SSL连接到ImgurAPI时出现错误。这是代码和错误:API_URI=URI.parse('https://api.imgur.com')API_PUBLIC_KEY='Client-ID--'ENDPOINTS={:image=>'/3/image',:gallery=>'/3/gallery'}#Public:Uploadanimage##args-Theimagepathfortheimagetoupload#defupload(image_path)http=Net::HTTP.new(API_URI.host)http.use_ssl=truehttp.verify
(本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展 是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。 如:有三个人,每个人做的不同的事物,但是是需要协作的完成。 而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
一、RIPV2协议简介 RIP(RoutingInformationProtocol)路由协议是一种相对古老,在小型以及同介质网络中得到了广泛应用的一种路由协议。RIP采用距离向量算法,是一种距离向量协议。RIP-1是有类别路由协议(ClassfulRoutingProtocol),它只支持以广播方式发布协议报文。RIP-1的协议报文无法携带掩码信息,它只能识别A、B、C类这样的自然网段的路由,因此RIP-1不支持非连续子网(DiscontiguousSubnet)。RIP-2是一种无类别路由协议(ClasslessRoutingProtocol),支持路由标记,在路由策略中可根据路由标记对
文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就