3.计算机网络

1.重点概念

MSL(Maximum segment lifetime):TCP 报⽂最⼤⽣存时间。它是任何 TCP 报⽂在⽹络上存在的 最⻓时间,超过这个时间报⽂将被丢弃。实际应⽤中常⽤的设置是 30 秒,1 分钟和 2 分钟。
TTL(Time to live):IP 数据报在⽹络中可以存活的总跳数,称为“⽣存时间”,但并不是⼀个真正的 时间。该域由源主机设置初始值,每经过⼀个路由器,跳数减 1,如果减⾄ 0,则丢弃该数据包,同时发送 ICMP 报⽂通知源主机。取值范围 1-255。
RTT(Round trip time):客户端到服务端往返所花时间。RTT 受⽹络传输拥塞的变化⽽变化,由TCP 动态地估算。
字节序:现代CPU的累加器⼀次都能装载(⾄少)8字节(64位机器),那么这8字节在内存中排序的顺序将影响它被累加器装载成的整数的值,这就是字节序问题。字节序分为⼤端字节序和⼩端字节序。⼤端字节序 是指⼀个整数的⾼位字节存储在内存的低地址处,低位字节存储在内存的⾼地址处。⼩端字节序反之。现代PC⼤多采⽤⼩端字节序,因此 ⼩端字节序 ⼜被称为 主机字节序。需要指出的是,即使是同⼀台机器上的两个进程通信,也要考虑字节序的问题(JAVA虚拟机采⽤⼤端字节序)。⽹际协议使⽤⼤端字节序来传送数据。
void show_bytes(char* start, size_t len) {
 for (size_t i = 0; i < len; i++) {
 printf("%.2x ", start[i]);
 }
}
void show_int(int x) {
 show_bytes((char*)&x, sizeof(int));
}
int main() {
 show_int(12345);
 return 0;
}
12345 的⼗六进制表示为 0x00003039,在不同机器上的输出值如下:【⾼地址--->低地址】

 

Linux 上先输出最低有效字节,说明它是⼩端法机器。
正向代理:要求客户端⾃⼰设置代理服务器的地址。客户的每次请求都将直接发送到该代理服务器,并由代理服务器请求⽬标资源。⽐如处于防⽕墙内的局域⽹机器要访问互联⽹,或者要访问⼀些被屏蔽掉
的国外⽹站,就需要使⽤正向代理服务器。
反向代理:被设置在服务端,因⽽客户端⽆须进⾏任何设置。反向代理是指⽤代理服务器来接受互联⽹上的连接请求,然后将请求转发给内部⽹络上的服务器,并将从内部服务器上得到的结果返回给客户端。这种情况下,代理服务器对外就表现为⼀个真实的服务器。各⼤⽹站通常分区域设置了多个代理服务器,所以在不同地⽅ping同⼀个域名可能得到不同的 IP 地址,因为这些 IP 地址实际上是代理服务器的 IP 地址。

 从输⼊⼀个 URL 到⻚⾯加载完成的过程:

浏览器:
浏览器是多进程的。浏览器只有⼀个主进程,负责创建/管理其他进程、显示浏览器主窗⼝、下载⽹络资源等。
每打开⼀个标签⻚,就创建了⼀个独⽴的浏览器渲染进程。浏览器渲染进程⼜称为浏览器内核,其内部是多线程的,负责⻚⾯渲染、脚本执⾏等。
浏览器的每⼀个⽹络请求,都需要主进程开辟⼀个单独的⽹络线程去处理。输⼊ URL 相当于⼀个⽹络请求,解析 HTML 时遇到 JS/CSS/图⽚等静态资源的引⽤链接,也需要分别请求。
服务器:
负载均衡:⼤型的项⽬,往往是由多台服务器组成⼀个集群,以此应对庞⼤的并发访问量。这种情况下,⽤户的请求都会指向调度服务器,由调度服务器将请求分发给集群中的某台服务器 A,然后调度服务器会将 A 服务器的 HTTP 响应发送给⽤户。负载均衡算法有:
1. 轮询(Round Robin):把每个请求轮流发送到每个服务器上。
2. 加权轮询(Weighted Round Robbin):根据服务器性能对其加⼊权重
3. 最少连接(Least Connections)
4. 加权最少连接(Weighted Least Connection)
5. 随机算法(Random)
6. 源地址哈希法(IP Hash):对客户端IP计算哈希值之后,再对服务器数量取模得到⽬标服务器的序号。可以保证同-IP的客户端的请求会转发到同⼀台服务器上,⽤来实现会话粘滞(Sticky Session)

2 OSI 七层模型

1. 物理层:确保原始的数据可以在各种物理媒介上传输。这⼀层是 bit 流。传输协议有 IEEE 802.1A等
2. 数据链路层:在不可靠的物理介质上提供可靠的传输。包括物理地址寻址、流量控制、数据的检错和重发等。这⼀层将 bit 流封装成 frame 帧。传输协议有 MAC 等
3. ⽹络层:负责对⼦⽹间的数据包进⾏路由选择,⽬的是实现两个端系统之间的数据透明传送。这⼀层数据的单位为数据包。协议有 IP、ICMP、ARP、RARP 等
4. 传输层:负责将上层数据分段并提供端到端的传输。这⼀层数据的单位称为数据段(segment)。传输协议有 TCP、UDP 等
5. 会话层:管理主机之间的会话进程,如建⽴会话、session认证、断点续传等。传输协议有 SMTP等
6. 表示层:主要解决⽤户信息的语法表示问题。数据的压缩和解压缩、加密和解密等⼯作由该层负
责。
7. 应⽤层:为操作系统或⽹络应⽤程序提供访问⽹络服务的接⼝。传输协议有 FTP、HTTP、
Telnet、DNS等。

2.2 TCP/IP 四层模型

TCP/IP 协议族是四层协议系统,⾃底向上分别是 数据链路层(物理层+数据链路层)、⽹络层、传输 层 和 应⽤层。

2.3 TCP  UDP

唯一确定TCP:协议类型 、源ip地址  目标ip地址 、源端口 、 目标端口

TCP 协议

TCP 为什么是面向连接的?

1. 发送之前需要先建立连接( 三次握手)

2. 使用排序和确认机制( 分组之间并不是独立的;记录了分组之间的状态信息)

3. 具有流量控制与拥塞控制

4. 发送完毕后要释放连接( 四次挥手)

TCP 如何保证传输的可靠性?

1. 三次握手: 确认通信实体存在

2. 序列号:解决乱序问题

3. 确认号 / 超时重传机制:解决丢包问题

4. 数据校验( 校验和 保证传输数据的正确

有一个 IP 的服务器监听了一个端口  它的 TCP 的最大连接数是多少?

服务器通常固定在某个本地端口上监听, 等待客户端的连接请求 。因此, 客户端 IP  端口是可变的,

其理论值计算公式如下:

最大TCP连接数=客户端的IP数X客户端的端口数

 IPv4, 客户端的 IP 数最多为 2^32, 客户端的端口数最多为 2^16, 也就是服务端单机最大 TCP 

接数, 约为 2^48。

当然, 服务端最大并发 TCP 连接数远不能达到理论上限, 会受以下因素影响:

●    文件描述符限制, 每个 TCP 连接都是一个文件, 如果文件描述符被占满了, 会发生 too many open files Linux 对可打开的文件描述符的数量分别作了三个方面的限制:

1. 系统级: 当前系统可打开的最大数量, 通过 cat /proc/sys/fs/file-max 查看;

2. 用户级:指定用户可打开的最大数量, 通过 cat /etc/security/limits.conf 查看; 3. 进程级: 单个进程可打开的最大数量, 通过 cat /proc/sys/fs/nr_open 查看;

●     内存限制, 每个 TCP 连接都要占 一定内存, 操作系统的内存是有限的, 如果内存资源被占满后, 会发生 OOM。

TCP 首部格式

 20 字节固定首部:

●    序列号( 32bit 在建立连接时由计算机生成的随机数作为其初始值, 通过 SYN 包传给接收端主 机, 每发送一次数据, 就「累加」一次该「数据字节数」 的大小, 用于解决乱序问题 。例如序号为 301, 表示第一个字节的编号是301, 如果携带的数据长度为100字节, 那么下一个报文段的序号应  为401

●    确认号( 32bit 接收方对发送方TCP报文段的响应, 值为序号值加一。

●    首部长(4bit 标识首部有多大, 最大为15, 即60字节

●    标志位 URG 6bit 紧急标志, 用于保证 TCP 连接不被中断, 并且督促中间层设备尽快处理

●    标志位 ACK 6bit 标记确认号是否有效( 确认报文段), 用于解决丢包问题

●    标志位 PSH 6bit 提示接收端立即从缓冲区读走数据

●    标志位 RST 6bit 表示要求对方重新建立连接( 复位报文段)

●    标志位 SYN 6bit 发送/同步标志, 用来建立连接, ACK 标志位搭配使用 。A 请求与 B  立连接时, SYN=1 ,ACK=0; B 确认与 A 建立连接时, SYN=1 ,ACK=1

●    标志位 FIN 6bit 结束标志, 表示关闭一个 TCP 连接

●    窗口  16bit 接收窗口, 用于告知发送方该端还能接收多少字节数据, 用于解决流控

●    校验和( 16bit 接收端用CRC检验整个报文段有无损坏

    源端口 、目标端口

TCP  UDP 区别

●    TCP UDP都是传输层协议 TCP适用于对效率要求相对低, 但是对准确性要求高的场景, 或是要 求有连接的场景 。如文件传输 、发送邮件等 UDP适用于对效率要求相对高 对准确性要求相对低 的场景 。如即时通信 、直播等。

●    TCP首部20字节; UDP的首部只有8个字节。

●    TCP有连接 、可靠的 、面向字节流的; UDP是无连接的 、不可靠的 、面向报文的。

●    TCP有拥塞控制和流量控制, 因此传输速度慢; UDP没有较多约束, 传输速度快。

●    每一条TCP连接只能是点到点的; UDP支持一对一, 一对多, 多对一和多对多的交互通信。

●    分片策略不同 TCP 的数据大小如果大于 MSS 最大报文长度  则会在传输层进行分片, 目标 主机收到后, 也同样在传输层组装 TCP 数据包, 如果中途丢失了一个分片, 只需要传输丢失的这   个分片; UDP 的数据大小如果大于 MTU 最大传输大小) 大小, 则会在 IP 层进行分片 目标主  机收到后,  IP 层组装完数据, 接着再传给传输层

既然 IP 层会分片 ,为什么 TCP 层还需要 MSS 呢?

 

●    MTU:一个网络包的最大长度, 以太网中一般为 1500 字节;

●    MSS 除去 IP TCP 头部之后, 一个网络包所能容纳的 TCP 数据的最大长度;

如果将 TCP 的整个报文  头部 + 数据) 交给 IP 层进行分片:  IP 层有一个超过 MTU 大小的数据

TCP 头部 + TCP 数据) 要发送, 那么 IP 层就要进行分片, 把数据分片成若干片, 保证每一个分片   都小于 MTU 。把一份 IP 数据报进行分片以后, 由目标主机的 IP 层来进行重新组装后, 再交给上一层  TCP 传输层 。如果一个 IP 分片丢失 ,整个 IP 报文的所有分片都得重传 。因为 IP 层本身没有超时重传 机制, 它由传输层的 TCP 来负责超时和重传 。当接收方发TCP 报文  头部 + 数据) 的某一 片丢失  后, 则不会响应 ACK 给对方, 那么发送方的 TCP 在超时后, 就会重发  Γ整个 TCP 报文  头部 + 数   据)  。因此, 可以得知由 IP 层进行分片传输, 是非常没有效率的 。经过 TCP 层分片后, 如果一个    TCP 分片丢失后  进行重发时也是以 MSS 为单位, 而不用重传所有的分片, 大大提高了重传的效率。

TCP 流量控制

流量控制的目的是控制发送端的发送速度( 同时也受拥塞控制的影响), 使其按照接收端的数据处理速度来发送数据, 避免接收端处理不过来, 产生网络拥塞或丢包。

TCP 实现流量控制的关键是滑动窗口  Sliding Window 。发送端和接收端均有一个滑动窗口, 对应  一个缓冲区, 记录当前发送或接收到的数据 。接收端会在返回的 ACK 报文中包含自己可用于接收数据的缓冲区的大小。

 

 

 

对于发送端来说:

●    LastByteAcked 指已发送且收到 ACK 的最后一个位置

●    LastByteSent 指向已发送但还未收到 ACK 的最后一个位置

●    LastByteWritten 指向上层应用写入但还未发送的最后一个位置

对于接收端来说:

●    LastByteRead 指向 TCP 缓冲区中读到的位置

●    NextByteExpected 指向收到连续包的最后一个位置

●    LastByteRcvd 指向收到的包的最后一个位置

零窗口

如果接收端处理过慢, 那么 window 可能变为 0, 这种情况下发送端就不再发送数据了 。如何在接收端 window 可用的时候通知发送端呢?TCP 使用来 ZWPZero Window Probe 零窗口探针) 技术 。具 体是在发送端引入一个计时器, 每当收到一个零窗口的应答后就启动该计时器 。每间隔一段时间就主动 发送报文, 由接收端来 ACK 窗口大小 。若接收者持续返回零窗口  一般是 3 次), 则有的 TCP 实现

会发送 RST 断开连接。

TCP 拥塞控制

流量控制是接收端控制的, 拥塞控制是发送端控制的 。最终都是控制发送端的发送速率 。发送端维持一 个叫做拥塞窗口 cwnd congestion window 的状态变量 。拥塞窗口的大小取决于网络的拥塞程度  并且动态地在变化。

为什么要有拥塞控制 ,不是有流量控制了吗? 流量控制是避免  Γ发送方」 的数据填满  Γ接收方」 的缓 存, 但是并不知道网络的通畅情况 。拥塞控制的目的就是避免  Γ发送方」 的数据填满整个网络。

慢启动

连接建立时, 设置 cwnd = 1, 表示可以传一个 MSS 大小的数据 每经过一个 RTT, cwnd 会翻倍(  数增长) 。当 cwnd >= ssthresh (slow start threshold) 时, 进入拥塞避免阶段。

拥塞避免

每经过一个 RTT, cwnd = cwnd + 1( 线性增长)

超时重传

如果发送端超时还未收到 ACK 包, 就可以认为网络出现了拥塞 。这时会把 sshthresh 设为当前拥塞窗 口的一半, 重新开始慢启动过程

快速重传 / 快速恢复

在接收方, 要求每次收到数据都应该对最后一个已收到的有序数据进行确认, 如果发送方收到重复确认, 那么就可以知道下一个报文段丢失, 此时执行快速重传, 立即重传下一个报文段

在这种情况下, 只是丢失个别报文段, 而不是网络拥塞, 因此执行快速恢复, 令阈值为当前拥塞窗口的一半, 拥塞窗口大小等于阈值大小, 此时进入拥塞避免阶段。

TCP 三次握手

可以使用 tcpdump 抓包分析, Linux使用 netstat -napt 查看 TCP 连接状态。

在工作中 tcpdump 只是用来抓取数据包, 然后把抓取的数据包保存成 pcap 后缀的文件, 接着用 Wireshark 工具进行数据包分析 [1]

第一次握手: 客户端请求建立连接, 向服务端发送一 同步报文( SYN=1), 同时选择一个随机数seq = x 作为 初始序列号第二次握手: 服务端收到连接请求报文后, 如果同意建立连接, 则向客户端发送 同步确认报文 (SYN=1 ,ACK=1), 确认号  ack = x + 1, 同时选择一个随机数 seq = y 作为 始序列号

第三次握手  可携带数据) :客户端收到服务端的确认后, 向服务端发送一个 确认报文(ACK=1),  认号  ack = y + 1, 序列号  seq = x + 1这时就完成了三次握手, 连接建立成功 。随后, 客户端和服务端的序列号将分别从 x+2 和 y+1 开始进 行传输。为什么需要三次握手 ,而不是两次或四次?

●    最主要原因是防止  Γ历史连接」初始化了连接 。第三次握手可以避免历史连接的影响, 而从及时终 止历史连接 。而两次握手无法解决历史连接问题。

●    避免资源浪费 。如果只有  Γ两次握手」, 当客户端的 SYN 请求连接在网络中阻塞, 客户端没有接   收到 ACK 报文, 就会重新发送 SYN , 由于没有第三次握手, 服务器不清楚客户端是否收到了自己

TCP 四次挥手

发送的建立连接的 ACK 确认信号, 所以每收到一个 SYN 就只能先主动建立一个连接, 如果客户端

 SYN 阻塞了, 重复发送多次 SYN 报文, 那么服务器在收到请求后就会建立多个冗余的无效链

接, 造成不必要的资源浪费。

●    四次握手其实也能够可靠的同步双方的初始化序号, 但可以优化成一步, 所以就成了  Γ三次握手」 。而两次握手只保证了一 方的初始序列号能被对方成功接收, 没办法保证双方的初始序列号都能被确认接收。

为什么每次建立 TCP 连接时 ,初始化的序列号都要求不一样呢?

●    主要为了防止历史报文被下一个相同四元组的连接接收;

●    为了安全性, 防止黑客伪造的相同序列号的 TCP 报文被对方接收;

SYN 报文被丢弃的两种场景:

●    开启 tcp_tw_recycle 参数 ,并且在 NAT 网络环境下 ,造成 SYN 报文被丢弃 。如果开启了

recycle  timestamps 选项, 就会开启一种叫 per-host  PAWS 机制 。per-host 是对  Γ对端    IP  序列号回绕( PAWS) 检查」, 而非对  ΓIP + 端口」 四元组做 PAWS 检查 。但是如果客户端  网络环境是用了 NAT 网关, 那么客户端环境的每一 台机器通过 NAT 网关后, 都会是相同的 IP    址, 在服务端看来, 就好像只是在跟一个客户端打交道一样, 无法区分出来 Per-host PAWS 机   制利用TCP option里的 timestamp 字段的增长来判断干扰数据, timestamp 是根据客户端各自  CPU tick 得出的值 。当客户端 A 通过 NAT 网关和服务器建立 TCP 连接, 然后服务器主动关闭 并且快速回收 TIME-WAIT 状态的连接后, 客户端 B 也通 NAT 网关和服务器建立 TCP 连接,

注意客户端 A  客户端 B 因为经过相同的 NAT 网关, 所以是用相同的 IP 地址与服务端建立

TCP 连接, 如果客户端 B  timestamp  客户端 A  timestamp 小, 那么由于服务端的 per- host  PAWS 机制的作用, 服务端就会丢弃客户端主机 B 发来的 SYN 包。

●    TCP 两个队列满了( 半连接队列和全连接队列 造成 SYN 报文被丢弃 。若开启

tcp_syncookies 参数则可在半连接队列满的情况下继续建立连接, 前提是全连接队列不满 。而当全 连接队列满了则会丢弃 SYN 报文

在没有开启 TCP keepalive ,且双方一直没有数据交互的情况下 ,如果客户端的  Γ主机崩溃」  ,会发 生什么;那  Γ进程崩溃」 的情况呢?

●    客户端主机崩溃了, 服务端是无法感知到的, 在加上服务端没有开启 TCP keepalive 又没有数据 交互的情况下, 服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态, 直到服务端重启进

 。所以, 我们可以得知一个点, 在没有使用 TCP 保活机制且双方不传输数据的情况下,  方的 TCP 连接处在 ESTABLISHED 状态, 并不代表另一 方的连接一定正常。

●    使用 kill -9 来模拟进程崩溃的情况, 发现在 kill 掉进程后, 服务端会发送 FIN 报文, 与客户端进  行四次挥手 。所以, 即使没有开启 TCP keepalive 且双方也没有数据交互的情况下, 如果其中一方的进程发生了崩溃, 这个过程操作系统是可以感知的到的, 于是就会发送 FIN 报文给对方, 然后与对方进行 TCP 四次挥手。

什么是 SYN 攻击?如何防范?

SYN 攻击属于 DOS 攻击的一种, 它利用 TCP 协议缺陷, 通过发送大量的半连接请求, 耗费 CPU 内存资源。

原理:在三次握手过程中, 服务器发送 [SYN/ACK] 包( 第二个包) 之后 、收到客户端的 [ACK] 包(  三个包) 之前的 TCP 连接称为半连接( half-open connect), 此时服务器处于 SYN_RECV 等待客  户端响应) 状态 。如果接收到客户端的 [ACK], TCP 连接成功, 如果未接受到, 则会不断重发请求   直至成功 SYN 攻击的攻击者在短时间内伪造大量不存在的 IP 地址, 向服务器不断地发送 [SYN]   服务器回复 [SYN/ACK] 包, 并等待客户的确认 。由于源地址是不存在的, 服务器需要不断的重发直至  超时 。这些伪造的 [SYN] 包将长时间占用未连接队列, 影响了正常的 SYN 导致目标系统运行缓慢、

网络堵塞甚至系统瘫痪。

检测: 当在服务器上看到大量的半连接状态时, 特别是源 IP 地址是随机的, 基本上可以断定这是一次

SYN 攻击。

防范: 主要有两大类, 一类是通过防火墙 、路由器等过滤网关防护, 另一类是通过加固 TCP/IP 协议栈 防范, 如增加最大半连接队列 、开启tcp_syncookies功能 、减少SYN+ACK重传次数 。但 SYN 攻击不  能完全被阻止, 除非将 TCP 协议重新设计, 否则只能尽可能的减轻 SYN 攻击的危害。

 

到报文确认号;确认号 = 收到报文序列号 + 1)

第一次挥手:客户端向服务端发送 连接释放报文( FIN=1 ,ACK=1), 主动关闭连接, 同时等待服务端的确认

●    序列号 seq = m, 即客户端上次发送的报文的最后一个字节的序号 + 1

●    确认号 ack = n, 即服务端上次发送的报文的最后一个字节的序号 + 1

第二次挥手: 服务端收到连接释放报文后, 立即发出 确认报文(ACK=1), 序列号 seq = n, 确认号    ack = m + 1 。这时 TCP 连接处于半关闭状态, 这表示客户端已经没有数据发送了, 但是服务端可能还 要给客户端发送数据。第三次挥手: 服务端向客户端发送 连接释放报文( FIN=1 ,ACK=1), 主动关闭连接, 同时等待客户端的确认

●    序列号 seq = p, 即服务端上次发送的报文的最后一个字节的序号 + 1 。此时处于半连接状态,  服务端无数据发送,  p == n + 1

    确认号 ack = m + 1, 与第二次挥手相同, 因为这段时间客户端没有发送数据

第四次挥手:客户端收到服务端的连接释放报文后, 立即发出 确认报文(ACK=1), 序列号 seq = m + 1, 确认号为 ack = p + 1 。此时, 客户端就进入 TIME-WAIT 状态 。注意此时客户端的 TCP 连接还没有释放, 必须经过 2*MSL 最长报文段寿命) 的时间后, 才进入 CLOSED 状态 。而服务端只要收 到客户端发出的确认, 就立即进入 CLOSED 状态。

为什么需要四次挥手

●    TCP 是全双工的,  方关闭连接后, 另一 方还可以继续发送数据 。所以四次挥手, 将断开连接分成 两个独立的过程。

●    第三次挥手前可能还要数据进行向请求关闭方发送。

服务端大量 CLOSE-WAIT 原因

在收到客户端关闭连接以及服务器关闭连接之间, 业务代码处理耗时较多造成的 。可启动异步处理后续

程序避免大量 CLOSE-WAIT 状态。

客户端 TIME_WAIT 过多有什么危害?

●     内存资源占用, 比如文件描述符 、内存资源 CPU 资源 、线程资源等 。;

●    对端口资源的占用, 一个 TCP 连接至少消耗「发起连接方」 的一个本地端口;

客户端 TIME-WAIT 状态必须等待 2MSL 原因

●    确保 ACK 报文能够到达服务端 ,从而使服务端正常关闭连接 。第四次挥手时, 客户端第四次挥手  ACK 报文不一定会到达服务端 。服务端会超时重传 FIN/ACK 报文, 此时如果客户端已经断开  了连接, 那么就无法响应服务端的二次请求, 这样服务端迟迟收不到 FIN/ACK 报文的确认, 就无  法正常断开连接 MSL 是报文段在网络上存活的最长时间, 客户端等待 2MSL 时间, 即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」, 就能够收到服务端重传的 FIN/ACK 报    文, 然后客户端重传一次 ACK 报文, 并重新启动 2MSL 时器 。如此保证服务端能够正常关闭。

●    防止已失效的连接请求报文段出现在之后的连接中 TCP 要求在 2MSL 内不使用相同的序列号  客户端在发送完最后一个 ACK 报文段后, 再经过时间 2MSL 就可以保证本连接持续的时间内产 生的所有报文段都从网络中消失 。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。 或者即使收到这些过时的报文, 也可以不处理它。

SO_REUSEADDR SO_REUSEPORT 问题[1]

1. SO_REUSEADDR

●    解决 server 重启的问题:server 端调用  close client 还没有调用 close  server  socket 处于 FIN_WAIT2 状态, 持续时间60s, 此时 server 重启会失败, bind时会报错 Address

already in use 。或者 server 端先调用 close, client 后调用close,  server 会处于

TIME_WAIT 状态, 持续时间也是60s, 此时 server 重启会失败, bind时也会报Address already in use 。解决方法:套接字bind前设置SO_REUSEADDR 或者 SO_REUSEPORT

●    解决 ip 为零的通配符问题:例如: socketA 绑定了0.0.0.0:2222, socketB 绑定

10.164.129.22:2222时, 或者相反情况, 都会报错 Address already in use 。因为 0.0.0.0 相当于 通配符, 可以匹配到10.164.129.22, 在没有设置地址复用或者端口复用前就会有此问题 。解决方

法: 方案1:socketA  socketB  bind 前设置 SO_REUSEADDR 并且 socketB 必须在

socketA 调用 listen 前调用 bind 。方案2: socketA  socketB  bind 前均设置了

SO_REUSEPORT 。方案3: socketB 在调用 bind 前设置进行强制bind 而不用管 socketA 是什么 状态。

2. SO_REUSEPORT:支持多个进程或者线程绑定到同一端口, 提高服务器程序的性能 。可以在调用 bind 绑定端口号之前进行设置

●    允许多个套接字 bind()/listen() 同一个TCP/UDP 端口: 每一个线程拥有自己的服务器套接字,  服务器套接字上没有了锁的竞争;

●     内核层面实现负载均衡(“惊群”的解决方法之一 );

●    安全层面 ,监听同一个端口的套接字只能位于同一个用户下面。

 

TCP 粘包问题

TCP 的数据块是没有边界 、没有结构的字节流, 因此可能产生粘包:发送方为了将多个发往接收端的包 更有效的发到对方, 使用了优化方法( Nagle算法), 将多次间隔较小 、数据量小的数据包, 合并成一   个大的数据包一次性发送 。接收方不能及时读取数据, 导致缓冲区中的多个包粘连。

解决方法:

1. 发送方关闭 Nagle 算法( 使用 TCP_NODELAY 可以禁用Nagle算法)

2. 应用层定义消息边界, 最常见的两种解决方案就是基于长度或者基于终结符

●    基于长度的实现有两种方式, 一种是使用固定长度; 另一种方式是使用不固定长度, 但是需要在应 用层协议的协议头中增加表示负载长度的字段, HTTP 协议的消息边界就是基于长度实现的;

●    HTTP 协议除了使用基于长度的方式实现边界, 也会使用基于终结符的策略,  HTTP 使用块传输 机制时, HTTP 头中就不再包含 Content-Length 了, 它会使用负载大小为 0  HTTP 消息作为  终结符表示消息的边界。

3. 基于特定的规则实现消息的边界, 例如:使用 TCP 协议发送 JSON 数据, 接收方可以根据接收到

的数据是否能够被解析成合法的 JSON 判断消息是否终结。

为什么 UDP 协议没有粘包问题?

UDP 是面向报文的, 应用层交给 UDP 多长的报文, UDP 就照样发送, 既不合并, 也不拆分, 而是保留这些报文的边界。

TCP Socket交互流程

 

服务器:

1. 创建socket         int socket(int domain, int type, int protocol);

domain :协议域, 决定了socket的地址类型, IPv4为AF_INET

type:指定socket类型, SOCK_STREAMTCP连接

protocol:指定协议 IPPROTO_TCP表示TCP协议, 为0表示默认协议

2. 绑定socket和端口号     int bind(int sockfd, const struct sockaddr* addr, socklen_t addr len); sockfdsocket返回的套接字描述符, 类似于文件描述符

addr :有个sockaddr类型数据的指针, 指向的是被绑定结构变量

addr len :地址长度。

1  // IPv4的sockaddr地址结构
2  struct sockaddr_in {
3       sa_family_t sin_family;  // 协议类型,AF_INET
4       in_port_t sin_port;  // 端口号
5       struct in_addr sin_addr;  // IP地址
6  };
7  struct in_addr {
8       uint32_t s_addr;
9  }

 

3. 监听端口号 int listen(int sockfd, int backlog);

sockfd:要监听的sock描述字

backlog:accept队列( 将要建立连接的队列) 长度=min(somaxconn, backlog), 其中 somaxconn是内核参数

4. 接收用户请求      int accept(int sockfd, struct sockaddr *addr, socklen_t *addr len);  三次握

手完成后)

sockfd 服务器socket描述字

addr :指向地址结构指针

addr len :协议地址长度

5. socket中读取字符    size_t read(int fd, void *buf, size_t count);

fd 连接描述字

buf:缓冲区

count:缓冲区长度

6. 关闭socket         int close(int fd);

fdaccept返回的连接描述字, 每个连接有一个 。而sockfd是监听描述字, 一个服务器只有一个, 用于监听是否有连接

客户机:

1. 创建socket

2. 连接指定计算机

握手完成后)

sockfd:客户端的sock描述字

addr :服务器地址

addr len :socket地址长度

3. socket写入信息        size_t write(int fd, const void *bud, size_t count);

4. 关闭socket         int close(int fd)

close  shutdown

close 关闭本进程的 socket id , 但链接还是开着的( 根据计数值决定是否关闭), 用这个 socket id 的其他进程还能用这个链接, 能读/写这个 socket idshutdown 则破坏了 socket 链接, 读的时候可能检测到EOF结束符, 写的时候可能会收到一个信号,这个信号可能直到 socket 缓冲区被填充了才收到, 其次还有关闭链接类型的参数, 0 不能再读, 1 不能 再写, 2 读写都不能。

UDP 协议

首部字段只有 8 个字节, 包括源端口 、 目的端口 、长度 、校验和。

2.4 HTTP

HTTP 是超文本传输协议, 是一个在计算机世界里专门在 「两点」 之间「传输」文字 、图片 、音频 、视 频等「超文本」数据的「约定和规范」 

HTTP 请求方法

●    GET:获取服务器的指定资源

●    HEAD:与 GET 方法一样, 都是发出一个获取服务器指定资源的请求, 但服务器只会返回 Header 而不会返回 Body 。用于确认 URI 的有效性及资源更新的日期时间等 。一个典型应用是下载文件

时, 先通过 HEAD 方法获取 Header 从中读取文件大小 Content-Length;然后再配合 Range 字段, 分片下载服务器资源

●    POST:提交资源到服务器 / 在服务器新建资源

    PUT:替换整个目标资源

●    PATCH:替换目标资源的部分内容

●    DELETE:删除指定的资源

●    OPTIONS 可以检测服务器支持哪些 HTTP 方法

●    TRACE:执行一个消息环回测试, 返回到服务端的路径 。客户端请求连接到目标服务器时可能会通 过代理中转, 通过 TRACE 方法可以查询发送出去的请求的一系列操作幂等的

int socket(int domain, int type, int protocol);

int connect(int sockfd, struct sockaddr *addr, socklen_t addr len);( 二次

一个 HTTP 方法是幂等的, 指的是同样的请求执行一次与执行多次的效果是一样的 。换句话说就是, 

等方法不应该具有副作用。

●    常见的幂等方法:GET HEAD PUT DELETE OPTIONS

     常见的非幂等方法: POST

安全的

一个 HTTP 方法是安全的, 指这是一个对服务器只读操作的方法, 不会修改服务器数据。

●    常见的安全方法:GET HEAD OPTIONS

●    常见的不安全方法: PUT DELETE POST

所有安全的方法都是幂等的;有些不安全的方法如 DELETE 是幂等的, 些不安全的方法如 PUT 

DELETE 则不是。

可缓存的:GET HEAD

GET  POST 区别

GET

POST

作用

获取服务器资源, 可被缓存

添加 / 修改服务器资源, 不能被缓存

幂等 / 安全性

幂等, 安全( 不会改变服务器上的资

源)

非幂等, 不安全( 会对服务器资源进行改

变)

参数位置

明文暴露在URL链接中

消息体中

参数长度

2KB HTTP本身无限制, 浏览器限

制)

无限制

 

HTTP 状态码

信息响应( 100– 199)

●    100 Continue :表明到目前为止都很正常, 客户端可以继续发送请求或者忽略这个响应 成功响应( 200–299)

    200 OK

●    204 No Content:该请求已成功处理, 但响应头没有 body 数据 。通常用于只需要从客户端往服务 器发送信息, 而不需要返回数据时

●    206 Partial Content 是应用于 HTTP 分块下载或断点续传, 表示响应返回的 body 数据并不是资 源的全部, 而是其中的一部分, 也是服务器处理成功的状态。

重定向( 300–399)

●    301 Moved Permanently:永久性重定向 。说明请求的资源已经不存在了, 需改用新的 URL 再次访问。

●    302 Found 临时性重定向, 说明请求的资源还在, 但暂时需要用另一个 URL 来访问 。常见应用 场景是通过 302 跳转将所有的 HTTP 流量重定向到 HTTPS

●    304 Not Modified 响应不包含消息体 。不具有跳转的含义, 表示资源未修改, 重定向已存在的缓 冲文件, 也称缓存重定向, 也就是告诉客户端可以继续使用缓存资源, 用于缓存控制。

客户端错误(400–499)

●    400 Bad Request:请求报文中存在语法错误, 或者参数有误

●    403 Forbidden :表示服务器禁止访问资源, 并不是客户端的请求出错。

●    404 Not Found:表示请求的资源在服务器上不存在或未找到, 所以无法提供给客户端。

服务器错误 ( 500–599)

●    500 Internal Server Error:发生不可预知的错误。

●    501 Not Implemented:表示客户端请求的功能还不支持, 类似“即将开业, 敬请期待”的意思。

●    502 Bad Gateway 通常是服务器作为网关或代理时返回的错误码, 表示服务器自身工作正常, 访 问后端服务器发生了错误。

●    503 Service Unavailable :表示服务器当前很忙, 暂时无法响应客户端, 类似“网络服务正忙,  稍后重试”的意思。

301 、302重定向的原理: 返回的 Header 中有一个 Location 字段指向目标 URL 浏览器会重定向到 这个 URL

HTTP 缓存

浏览器可以将已经请求过的资源( 如图片 JS 文件) 缓存下来, 下次再次请求相同的资源时, 直接从缓

存读取 。浏览器采用的缓存策略有两种: 强制缓存 、协商缓存 。浏览器根据第一次请求资源时返回的

HTTP 响应头来选择缓存策略 。强制缓存优先级大于协商缓存。

强制缓存

强缓存是利用 HTTP 响应头部字段实现的, 它们都用来表示资源在客户端缓存的有效期:

●    Cache-Control 是一个相对时间;Cache-Control的优先级高于 Expires 

    Expires 是一个绝对时间;

具体的实现流程如下:

1. 当浏览器第一次请求访问服务器资源时, 服务器会在返回这个资源的同时, 在响应头部加上 Cache-Control 字段, 其中设置了过期时间;

2. 浏览器再次请求访问服务器中的该资源时, 会先通过请求资源的时间与 Cache-Control 中设置的 过期时间来计算出该资源是否过期, 如果没有, 则使用该缓存, 否则重新请求服务器;

3. 服务器再次收到请求后, 会更新响应头部的 Cache-Control

协商缓存

协商缓存可以基于两种头部来实现。

●    第一种:请求头部中的 If-Modified-Since 字段与响应头部中 Last-Modified 字段实现 。响应 头部中的 Last-Modified 标示这个响应资源的最后修改时间;请求头部中的 If-Modified-Since   表示当资源过期时, 发现响应头中具有 Last-Modified 声明 则再次发起请求的时候带上 Last-   Modified 的时间, 服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间  进行对比, 如果最后修改时间较新( ), 说明资源又被改过 则返回最新资源, HTTP 200

OK 如果最后修改时间较旧( ), 说明资源无修改, 响应 HTTP 304 走缓存。

●    第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段( 唯一标识响应资源)  其中请求头部中的 If-None-Match 表示当资源过期时, 浏览器发现响应头里有 Etag 则再次向服 务器发起请求时, 会将请求头If-None-Match 值设置为 Etag 的值 。服务器收到请求后进行比对  如果资源没有变化返回 304, 如果资源变化了返回 200。

第一种实现方式是基于时间实现的, 第二种实现方式是基于一个唯一标识实现的, 相对来说后者可以更加准确地判断文件内容是否被修改, 避免由于时间篡改导致的不可靠问题。

注意, 只有在未能命中强制缓存的时候, 才能发起带有协商缓存字段的请求。

HTTP 报文格式

请求报文

HTTP 协议以 ASCII 码传输, 请求报文由请求行 、请求头 、( 空行以及) 消息体组成 。一个消息主体一 定包含一个实体主体, 通常情况下消息主体等于实体主体, 实体主体也可能经过传输编码机制处理, 通信时按某种编码方式传输。

 

响应报文

HTTP 响应报文也由三部分组成:状态行 、响应头 、( 空行以及) 消息体。

HTTP 分块传输编码

 HTTP 通信过程中, 请求的编码实体资源尚未全部传输完成之前, 浏览器无法显示请求页面 。在传输 大容量数据时, 通过把数据分割成多块, 能够让浏览器逐步显示页面 。这种把实体主体分块的功能称为  分块传输编码( Chunked Transfer Coding 。它通过在 Header 里两个参数实现的, 客户端发请求时 对应的是 Range , 服务器端响应时对应的是 Content-Range Range 用于请求头中, 定第一个字  节的位置和最后一个字节的位置 Content-Range 用于响应头中, 在发出带 Range 的请求后, 服务器 会在 Content-Range 头部返回当前接受的范围和文件总大小 [1]

HTTP 常见字段:

●    Host 字段:客户端指定服务器的域名, 可以将请求发往  Γ同一 台」 服务器上的不同网站。

●    Content-Length 字段: 服务器返回数据的长度。

●    Content-Type 字段: 服务器回应客户端的数据格式 。客户端请求的时候, 可以使用 Accept 字段 声明自己可以接受哪些数据格式。

●    Content-Encoding 字段: 服务器返回数据使用的压缩格式 。客户端在请求时, Accept- Encoding 字段说明自己可以接受哪些压缩方法。

●    Connection 字段: 最常用于客户端要求服务器使用 TCP 持久连接, 以便其他请求复用。

HTTP/1.1 版本的默认连接都是持久连接, 但为了兼容老版本的 HTTP 需要指定 Connection 首部 字段的值为 Keep-Alive

Cookie  Session 区别

二者都是用来跟踪浏览器用户身份的会话方式。

Cookie:存在浏览器里, 可以设置过期时间 。每次访问服务器时, 浏览器会自动在 header 中携带 cookie 。如果浏览器禁用了 cookie 可以使用 URL 地址重写机制, 将信息保存在 URL 

Session:存在服务端, 由服务器维护, 服务端设置过期时间 。如果用户长时间不和服务器交互( 比如 30 分钟), 那么 session 就会被销毁, 交互则会刷新 session 。浏览器的 cookie 只保存一个sessionId, 所有其他信息均保存在服务端,  sessionId 标识。

 

Manager 充当一个 session 管理器的角色, 主要存储一些配置信息, 比如 session 存活时间,

cookie 的名字等等 。而所有的 session 存在 Manager 内部的一个 Provider  。所以 Manager 会把 sid sessionID 传递给 Provider 让它去找这个 ID 对应的具体是哪个 session

Provider 就是一个容器, 最常见的应该就是一个散列表, 将每个 sid 和对应的 session 一一 映射起 。收到 Manager 传递的 sid 之后, 它就找到 sid 对应的 session 结构 也就是 Session 结构, 然后 返回它。

Session 中存储着用户的具体信息,  Handler 函数中的逻辑拿出这些信息, 生成该用户的 HTML  页, 返回给客户端。

既然 session 就是键值对 ,为啥不直接用哈希表?

1. 可以存储一些辅助数据, 比如 sid 访问次数, 过期时间或者最后一次的访问时间, 这样便于实现  LRU LFU 这样的算法。

2. 可以有不同的存储方式, 比如存入缓存数据库 Redis 或者存入 MySQL 等等 。如果用编程语言内 置的哈希表, 那么 session 数据就是存储在内存中, 如果数据量大, 很容易造成程序崩溃, 而且一 旦程序结束, 所有 session 数据都会丢失。

Provider 为啥要抽象出来?

上图的 Provider 就是一个散列表, 保存 sid  Session 的映射, 但是实际中肯定会更加复杂 。我们不 是要时不时删除一些 session 吗, 除了设置存活时间之外, 还可以采用 一些其他策略,  LRU 缓存 淘汰算法, 这样就需要 Provider 内部使用哈希链表这种数据结构来存储 session

Manager 为啥要抽象出来?

大部分具体工作都委托给 Session  Provider 承担了, Manager 主要就是一个参数集合, 比如

session 的存活时间, 清理过期 session 的策略, 以及 session 的可用存储方式 Manager 屏蔽了操 作的具体细节, 我们可以通过 Manager 灵活地配置 session 制。

HTTP/1.1

HTTP/1.1

优点: 简单 、灵活和易于扩展 、应用广泛和跨平台

缺点: 无状态 、明文传输 、不安全

HTTP/1.1 相比 HTTP/1.0 性能上的改进:

●    使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销, 不过长连接会占用服务器的资源。

●    支持管道( pipeline 网络传输, 只要第一个请求发出去了, 不必等其回来, 就可以发第二个请求 出去, 可以减少整体的响应时间。

性能瓶颈:

●    请求 / 响应头部 未经压缩就发送, 首部信息越多延迟越大, 且每次互相发送相同的首部造成的浪费 较多;

●    服务器是按请求的顺序响应的, 如果服务器响应慢, 会导致客户端一直请求不到数据, 也就是队头 阻塞;

●    没有请求优先级控制;

●    请求只能从客户端开始, 服务器只能被动响应。

如何优化?

●    尽量避免发送 HTTP 请求:尽量使用缓存处理。

●    尽量减少请求次数:利用代理服务器等减少重定向请求次数;合并请求资源;延迟发送请求。

●    减少响应的数据大小: 通过有损或无损压缩对响应的资源进行压缩。

HTTP/2.0

HTTP/2.0 改进点 [基于 HTTPS ]

1. Header 压缩: 头部的编码通过「静态表( 保存常用字段编码并固定在协议中) 、动态表( 动态添 加静态表没有的字段编码) Huffman 编码」 共同完成的。

2. 二进制分帧: HTTP/1.x 采用文本格式传输数据 HTTP/2.0 将所有传输信息分割为若干个帧,  用二进制格式进行编码 。具体实现上, 是在应用层( HTTP 和传输层(TCP 之间增加一个二进 制分帧层 。每个请求对应一个流, 有个唯一 的标识符 。请求报文会被拆分为一个或多个帧( 帧头   +消息负载), 每个帧有序列号, 以及自己所属流的标识符, 接收端自行合并 。同时, 二进制分帧

采用的流传输也为多路复用提供了基础。

3. 多路复用:每个请求或响应的数据包称为一个数据流, 每个数据流都有唯一 的编号, 因此不同流的 帧是可以乱序发送的( 即可以并发不同的流) 。因为每个帧的头部会携带流编号信息, 所以接收端 可以通过流编号有序拼接 HTTP 消息 。客户端和服务器双方都可以建立流, 其中客户端建立的流

编号必须是奇数号, 而服务器建立的流编号必须是偶数号 。客户端还可以指定数据流的优先级。

4. 服务端推送: 服务端根据客户端的请求, 提前推送额外的资源给客户端, 可以减轻数据传输的冗余 步骤, 同时加快页面响应速度, 提升用户体验 。比如在发送页面 HTML 主动推送其它 CSS/JS   资源, 而不用等到浏览器解析到相应位置, 发起请求再响应。

HTTP/2.0 缺陷

●    HTTP 队头阻塞 HTTP/2 是基于 TCP 协议来传输数据的 TCP 是字节流协议 TCP 层必须保证 收到的字节数据是完整且连续的, 这样内核才会将缓冲区里的数据返回给 HTTP 应用, 那么当

「前 1 个字节数据」没有到达时, 后收到的字节数据只能存放在内核缓冲区里, 只有等到这 1 个字  节数据到达时, HTTP/2 应用层才能从内核中拿到数据, 这就是 HTTP/2 队头阻塞问题 。所以,  旦发生了丢包现象, 就会触发 TCP 的重传机制, 这样在一个 TCP 连接中的所有的 HTTP 请求都

必须等待这个丢了的包被重传回来。

●    慢启动降低效率 TCP 由于具有「拥塞控制」 的特性, 以刚建立连接的 TCP 会有个「慢启动」 的过程, 它会对 TCP 连接产生 "减速"效果。

●     网络切换重连 。一个 TCP 连接是由四元组(  IP 地址, 源端口, 目标 IP 地址, 目标端口 确定 的, 这意味着如果 IP 地址或者端口变动了, 就会导致需要 TCP TLS 重新握手 这不利于移动 设备切换网络的场景, 比如 4G 网络环境切换成 WIFI

HTTP/3 / QUIC

HTTP/3 优化?

●    改进头部压缩算法 HTTP/3 中的 QPACK 也采用了静态表 、动态表及 Huffman 编码 HTTP/2    HTTP/3 的动态表编解码方式不同, QUIC 会有两个特殊的单向流, 这两个特殊的单向流是用来 同步双方的动态表, 编码方收到解码方更新确认的通知后, 才使用动态表编码 HTTP 头部。

●    更换传输协议 HTTP/2 虽然通过多个请求复用 一个 TCP 连接解决了 HTTP 的队头阻塞 , 但是一 旦发生丢包, 就会阻塞住所有的 HTTP 请求, 这属于 TCP 层队头阻塞 。所以 HTTP/3  HTTP

下层的 TCP 协议改成了 UDP 基于 UDP  QUIC 协议 可以实现类似 TCP 的可靠传输。

QUIC 实现了两种级别的流量控制 ,分别为 Stream  Connection 两种级别:

●    Stream 流量控制:每个 HTTP 请求对应一个流, 每个 Stream 都有独立的滑动窗口, 所以每个 Stream 都可以做流量控制, 防止单个 Stream 占用连接的全部接收缓冲。

●    Connection 流量控制: 限制连接中所有 Stream 加起来的总字节数, 防止发送方超过连接的缓冲 容量。

QUIC 有以下 3 个特点

●    无队头阻塞: 当某个流发生丢包时, 只会阻塞这个流, 其他流不会受到影响, 因此不存在队头阻塞 问题 。而 HTTP/2 只要某个流中的数据包丢失了, 其他流也会受影响。

●    更快的建立连接:对于 HTTPS  HTTP/2 协议 TCP TLS 是分层的, 需要分批次来握手,  TCP 握手, TLS 握手 HTTP/3 在传输数据前虽然需要 QUIC 协议握手, 这个握手过程只需要 1 RTT 握手的目的是为确认双方的「连接 ID」, 连接迁移就是基于连接 ID 实现的 。不过 QUIC    协议并不是与 TLS 分层, 其内部包含了 TLS 再加上 QUIC 使用的是 TLS/1.3, 因此仅需 1 

RTT 就可以「同时」完成建立连接与密钥协商。

●    连接迁移: 基于 TCP 传输协议的 HTTP 协议, 由于是通过四元组(  IP 、源端口 、  IP  目的

端口) 确定一条 TCP 连接, 那么当移动设备的网络从 4G 切换到 WIFI  意味着 IP 地址变化

了, 那么就必须要断开连接, 然后重新建立连接 。而建立连接的过程包含 TCP 三次握手和 TLS   次握手的时延, 以及 TCP 慢启动的减速过程, 给用户的感觉就是网络突然卡顿了一下, 因此连接    的迁移成本是很高的 。而 QUIC 协议没有用四元组的方式来“绑定”连接, 而是通过连接 ID来标记通 信的两个端点, 客户端和服务器可以各自选择一组 ID 来标记自己, 因此即使移动设备的网络 IP 址变化了, 只要仍保有上下文信息( 比如连接 ID 、TLS 密钥等), 就可以“无缝”地复用原连接,消除重连的成本, 没有卡顿感, 达到了连接迁移的功能。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt 基于海康相机的视频绘图

需求 在视频窗口上进行绘图&#xff0c;包括圆&#xff0c;矩形&#xff0c;扇形等 效果&#xff1a; 思路&#xff1a; 自己取图然后转成QImage &#xff0c;再向QWidget 进行渲染&#xff0c;根据以往的经验&#xff0c;无法达到很高的帧率。因此决定使用相机SDK自带的渲染…

DAY60 84.柱状图中最大的矩形

84.柱状图中最大的矩形 题目要求&#xff1a;给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 思路 单调栈 本地单调栈的解法和接雨水的题目是遥相呼…

【DevOps】Git 图文详解(七):标签管理

Git 图文详解&#xff08;七&#xff09;&#xff1a;标签管理 标签&#xff08;Tags&#xff09;指的是某个分支某个特定时间点的状态&#xff0c;是对某一个提交记录的 固定 “指针” 引用。一经创建&#xff0c;不可移动&#xff0c;存储在工作区根目录下 .git\refs\tags。可…

智能座舱架构与芯片- (4) 硬件篇 中

2.4 高速视频传输(GMSL) 为了解决未来汽车系统所面临的问题&#xff0c;美信(Maxim)推出了全新下一代GMSL技术&#xff0c;即吉比特多媒体串行链路(GMSL)串行器和解串器&#xff0c;用来支持未来ADAS和信息娱乐系统要求的宽带、互联复杂度和数据完整性的要求。 GMSL技术可以支…

Stock接口_节假日(1)

节假日 文章目录 节假日一. 查询最近十天的交易日日期列表二. 查询日期段内的交易日日期列表三. 查询假期信息 一. 查询最近十天的交易日日期列表 接口描述: 接口地址:/StockApi/holidayCalendar/getTenTradeDay 请求方式&#xff1a;GET consumes: produces:["*/*&q…

【C++】类和对象一

今天来到了类和对象部分&#xff0c;我们知道C语言是面向过程编程&#xff0c;而C是面向对象编程&#xff0c;那么怎么个具体实现方法呢&#xff1f;简单来说&#xff0c;就是C语言对结构体的定义和对结构体的操作是分开的&#xff0c;这样就显得过程很独立&#xff1b;而C是把…

Javaweb之Axios的详细解析

1.3 Axios 上述原生的Ajax请求的代码编写起来还是比较繁琐的&#xff0c;所以接下来我们学习一门更加简单的发送Ajax请求的技术Axios 。Axios是对原生的AJAX进行封装&#xff0c;简化书写。Axios官网是&#xff1a;https://www.axios-http.cn 1.3.1 Axios的基本使用 Axios的…

知识库文档处理

知识库文档处理 1 知识库设计2 文档加载2.1 PDF文档2.2 MD文档2.3 MP4视频 3 文档分割4 文档词向量化 本项目是一个个人知识库助手项目&#xff0c;旨在帮助用户根据个人知识库内容&#xff0c;回答用户问题。个人知识库应当能够支持各种类型的数据&#xff0c;支持用户便捷地导…

【Java程序员面试专栏 专业技能篇】Java SE核心面试指引(二):面向对象思想

关于Java SE部分的核心知识进行一网打尽,包括四部分:基础知识考察、面向对象思想、核心机制策略、Java新特性,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 本篇Blog为第二部分:面向对象思想,子节点表示追问或同级提问 面向对象基…

【用unity实现100个游戏之16】Unity程序化生成随机2D地牢游戏3(附项目源码)

文章目录 先本文看看最终效果前言二叉空间分割算法房间优先生成使用走廊连接各个房间BSP和随机游走源码完结 先本文看看最终效果 前言 前两期我们使用了随机游走算法已经实现了地牢的生成&#xff0c;本期再说另外一种生成地牢的方法&#xff0c;使用二叉空间分割算法&#xf…

Git——分布式版本控制工具

一、概述 1.开发中的实际场景 备份代码还原协同开发追溯问题代码的编写人和编写时间 2.版本控制器的方式 集中式版本控制工具 集中式版本控制工具&#xff0c;版本库是集中存放在中央服务器的&#xff0c;team里每个人work时从中央服务器下载代码&#xff0c;是必须联网才能…

nodejs微信小程序 +python+PHP- 校园志愿者管理系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

Go 语言中 For 循环:语法、使用方法和实例教程

for循环用于多次执行特定的代码块&#xff0c;每次都可以使用不同的值。每次循环执行都称为一次迭代。for循环可以包含最多三个语句&#xff1a; 语法 for 语句1; 语句2; 语句3 {// 每次迭代要执行的代码 }语句1&#xff1a;初始化循环计数器的值。语句2&#xff1a;对每次循环…

微信小程序如何使用scss,less

搜到很多都是先VSCode安装好…插件…。这都是很久之前的方法了&#xff0c;所以想写这篇文章 一、修改project.config.json配置文件 "setting": {"useCompilerPlugins": ["sass"]},二、然后就可以删除 .wxss 文件了&#xff0c;就用 .scss 文件…

腾讯极光盒子A4021增强版_线刷官方

1、用USB_Burning_Tool线刷提供的线刷包&#xff0c;所需资料地址在最后 1&#xff09;打开USB_Burning_Tool&#xff0c;选择资料里的A4021_line_flash_root.img&#xff08;文件夹最好没有中文字符和空格&#xff09;&#xff0c;然后点击【开始】。 2&#xff09;盒子准备好…

mac添加Chrome插件的方法

如果是.crx的插件 更改后缀crx为zip 后续步骤同下文.zip文件 如果是.zip的插件 使用终端进行解压 注意不要用解压工具解压&#xff0c;一定要用终端&#xff0c;命令行解压 // 进入到“插件名.zip”文件的目录下&#xff0c;输入下面命令&#xff1a; unzip 插件名.zip -…

LeetCode209.长度最小的子数组(滑动窗口法、暴力法)

LeetCode209.长度最小的子数组 1.问题描述2.解题思路3.代码4.知识点 1.问题描述 给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果…

阿里云优惠券如何领取(阿里云在哪领取优惠券)

阿里云优惠券是阿里云为了回馈广大用户而推出的一种优惠活动&#xff0c;可以帮助用户在购买阿里云产品和服务时享受一定的优惠&#xff0c;本文将为大家介绍如何领取阿里云优惠券。 1、通过阿里云官网活动页面领取 阿里云会不定期举办一些优惠活动&#xff0c;例如双十一、双…

C语言基本算法之选择排序

目录 概要&#xff1a; 代码如下 运行结果如下 概要&#xff1a; 它和冒泡排序一样&#xff0c;都是把数组元素按顺序排列&#xff0c;但是方法不同&#xff0c;冒泡排序是把较小值一个一个往后面移&#xff0c;选择排序则是直接找出最小值&#xff0c;可以这个说&#xff…

IDEA如何将本地项目推送到GitHub上?

大家好&#xff0c;我是G探险者。 IntelliJ IDEA 是一个强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它支持多种编程语言和工具。它也内置了对Git和GitHub的支持&#xff0c;让开发者可以轻松地将本地项目推送到GitHub上。以下是一个操作手册&#xff0c;描述了…