当前位置: 首页 > news >正文

传输层协议 TCP UDP

目录

协议前菜

端口号

​编辑端口号范围划分 

认识知名端口号(Well-Know Port Number) 

netstat

pidof 

传输层协议

UDP协议

UDP协议端格式

UDP的特点 

面向数据报

UDP的缓冲区

UDP使用注意事项

基于UDP的应用层协议

TCP协议

TCP协议概念

TCP协议段格式

标志位详解

确认应答(ACK)机制

序列号 && 确认序号

对于为什么有确认序号和序号

超时重传机制

连接管理机制(三次握手四次挥手)

服务端状态转化

客户端状态转化

为什么是三次?

两次呢?

四次挥手

下图是TCP状态转换的一个汇总

理解TIME_WAIT状态

为什么是TIME_WAIT的时间是2MSL

解决TIME_WAIT状态引起的bind失败的方法

理解 CLOSE_WAIT 状态 

滑动窗口

流量控制 

拥塞控制 

延迟应答 

捎带应答 

面向字节流

粘包问题

关于listen的第二个参数

TCP异常情况

TCP小结

基于TCP应用层协议

TCP/UDP对比


C语言总结 在这 常见八大排序 在这
作者和朋友建立的社区: 非科班转码社区-CSDN社区云 💖 💛 💙
期待hxd的支持哈 🎉 🎉 🎉
最后是打鸡血环节: 想多了都是问题,做多了都是答案 🚀 🚀 🚀
最近作者和好友建立了一个公众号
公众号介绍:

专注于自学编程领域。由USTC、WHU、SDU等高校学生、ACM竞赛选手、CSDN万粉博主、双非上岸BAT学长原创。分享业内资讯、硬核原创资源、职业规划等,和大家一起努力、成长。( 二维码在文章底部哈! )

协议前菜

端口号

端口号(Port)标识了一个主机上进行通信的不同的应用程序

TCP/IP协议中, "IP", "源端口号", "目的IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信(可以通过netstat -n查看)

端口号范围划分 

0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的。

1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的。

认识知名端口号(Well-Know Port Number) 

有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号:

执行下面的命令, 可以看到知名端口号

cat /etc/services

我们自己写一个程序使用端口号时, 要避开这些知名端口号

netstat

netstat是一个用来查看网络状态的重要工具

语法netstat [选项]
功能:查看网络状态
常用选项
  • n 拒绝显示别名,能显示数字的全部转化成数字
  • l  仅列出有在 Listen (监听) 的服務状态
  • p 显示建立相关链接的程序名
  • t  (tcp)仅显示tcp相关选项
  • u (udp)仅显示udp相关选项
  • a (all)显示所有选项,默认不显示LISTEN相关

netstat是一个用来查看网络状态的重要工具
netstat -nltp/ntp 常用(我们经常去启动服务时,想看服务是否成功启动,就带l,看是否LISTEN。看链接什么的就去掉l。)

pidof 

在查看服务器的进程id时非常方便
语法pidof [进程名]
功能:通过进程名, 查看进程id

传输层协议

1. 传输层根据传来的端口号,查哈希表找到对应的进程(进程和端口号是通过哈希关联的)
2. linux下一切皆文件,我们的网络套接字在底层也是以文件的形式存在的,就是文件描述符,也就是进程和文件的对应关系。换言之,我们只要能找到进程,我们就能找到该进程对应的网络文件,文件打开是有自己的内核缓冲区的,所以当网络收过来的数据在内核当中,他首先通过目的端口号找到目的进程,然后再根据目标进程所打开的文件,找到该文件打开的内核缓冲区,然后把数据填到缓冲区里,这样进程就可以以文件的方式读取文件了。
(根据目的端口号查hash表,找到进程,再根据进程结合文件描述符,找到文件,再把数据拷贝到文件缓冲区里,最后就跟读文件一样就把数据传上去了)
PS:1. 添加报头的本质就是拷贝对象
2. 在网络中发送数据(比如send)其实并不是我们发送的,是我们把数据拷贝到了TCP/UDP对应的缓冲区中,怎么发送/什么时候发都是OS决定的。(我们讲文件的时候,其实并不是我们把数据写到了磁盘/文件里,而是我们把数据写到了OS的缓冲区中(比如write),然后OS经过自己的策略把数据刷新到外设比如磁盘上(所以write其实是拷贝函数,是把应用层的数据拷贝到内核,再有OS定向的用自己的策略发送到磁盘上))

UDP协议

UDP协议端格式

16UDP长度, 表示整个数据报(UDP首部+UDP数据)的最大长度。
如果校验和出错, 就会直接丢弃。

UDP的特点 

无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;
面向数据报: 不能够灵活的控制读写数据的次数和数量;

面向数据报

应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并
UDP传输100个字节的数据:
如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个字节; 而不能循环调用10recvfrom, 每次接收10个字节;

UDP的缓冲区

UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;
UDP有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃
UDPsocket既能读, 也能写, 这个概念叫做 全双工。

UDP使用注意事项

我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)。
然而64K在当今的互联网环境下, 是一个非常小的数字。
如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装

基于UDP的应用层协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议
当然, 也包括你自己写UDP程序时自定义的应用层协议;

TCP协议

TCP协议概念

TCP全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制;
没有100%可靠的协议,但是有局部100%可靠的协议,虽然最新的消息我们没有应答,但是之前的消息我们可以做到,收到了应答,就说明我刚发的消息对方100%收到了。

TCP协议段格式

  • /目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
  • 32位序号/32位确认号: 后面详细讲;
  • 4TCP报头长度: 表示该TCP头部有多少个32bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60;
  • 6位标志位:
  •        URG:紧急指针是否有效。
  •         ACK: 确认号是否有效。
  •         PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走。
  •         RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段。
  •         SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段。
  •         FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段。
  • 16位窗口大小: 后面再说;
  • 16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分;
  • 16位紧急指针: 标识哪部分数据是紧急数据;
  • 40字节头部选项: 暂时忽略;

标志位详解

SYN:只要报文是建立链接的请求,SYN需要被设置为1。

FIN:该报文是一个断开链接的请求报文。

ACK:确认标记位,表示该报文算术对历史报文的确认(一般大部分正常通信的情况下,ACK都是1)

PSH:接受数据的低水位线:比如接受缓冲区可有100byte,但是要有20,才去通知上层有数据了。但是如果有PSH,他就会影响本地的OS让他尽快去通知上层,让其读取,这就是PSH状态位的含意。(以前我们都是自己阻塞去调用read的,但是如果我们以后不想阻塞等待,就需要有人尽快的或者使用OS默认的策略来(当数据有了或者超过数据低水位线了)通知你,而我们要是不想要OS默认,而是想立即去读,我们就把PSH标记位设置,此时这个数据就会被我们对于的server端收到之后,放到缓冲区里更多的做一个工作,通知上层来可以读取)(PSH(催促报文)是Client催Server去尽快拿接受缓冲区数据的,PSH是Client发送的。这是极端的情况,用于理解PSH,上面那个是准确的描述)

URG:紧急指针标记位,报文在发送的时候,我们是要保证按序到达的,也就是说如果有一些数据优先级更高,但是序号较晚,无法做到数据被有限紧急处理。

RST:如果客户端只要是发送了请求,就认为是连接建立成功了,但是如果最后发送的ACK丢失了呢?此时服务端因为没有收到ACK所以认为连接依旧没有建立成功,此时客户端给服务端发送消息,但是服务端看到客户端消息之后,发现并没有完成三次握手(要遵守协议),所以就意识到客户端三次握手可能失败了,就会给客户端发送ACK响应,响应的时候将响应报文的**RST**(reset)标记位置为一,代表的就是告诉客户端,将你的连接进行重置。(tcp报文中reset标记位被置一,代表需要关闭连接需要重新连接,所以叫reset)

PS:

在数据通信前就已经有了三次握手,里面就会协商双方的数据通信能力,所以不怕第一次发送数据时大于对方接受能力(不担心他来不及接受的问题)。
(PS:最后一次ACK(三次握手时)也可以携带数据)

如何做到?
那tcp有些场景,有可能有些数据是需要server端优先去读取,那么我们就需要把tcp的标志位设置为URG,那么就代表该数据可以直接忽略他的序号而被上层直接进行读取处理,这样的报文就叫做我们的紧急指针报文。(注意设置了之后代表的是可以被优先读取)
但是又有问题,那么这些数据都是在接受缓冲区的,你说要紧急读取,那他在哪呢?
这就有了我们的16位紧急指针!他就代表我们的要紧急读取的数据在缓冲区特定偏移量的位置。那多大呢?注意,只能有一个字节,也就是说,紧急发送的数据,只能有一个字节。
那什么情况下会这样用呢?
首先%99情况用不到。其次一般用紧急指针传递的数据都是有额外含义的数据并不只是数据本身。(就有点像是宏一样,数字对应已经预定好的信息,目的就是为了未来主机或者服务出问题的时候,去获取他们的状态的)所以我们把URG标记所表示的数据一般称之为带外数据(就是用同一个tcp链接,不走你的接受缓冲区,而是被上层优先处理,这就是带外数据)带外数据通常用来检测已经毫无反应的机器的状态(已经挂掉不想/但是跑的很慢可以查到)(紧急指针在机房中的应用场景)。

确认应答(ACK)机制

序列号 && 确认序号

TCP将每个字节的数据都进行了编号. 即为序列号

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。

确认序号比对应的序号**大1**,并且代表**前面的**序号**都**确认了。(序号我们来代表一个报文所对应的序号,而确认序号是用来确认特定我所收到的报文之前的序号全部收到了(**比如C发送了 1 2 3 5 6 S返回的确认序号就是4!**))

对于为什么有确认序号和序号

因为TCP协议是全双工的,我在给你发消息的同时,我也可以收消息。

即服务器在确认消息的时候,同时发送了新的消息,给应答就要填充确认序号,而TCP是保证可靠性的,在服务器发消息那也需要序号,那就是说S要有序号和确认序号一起返回给C。即序号和确认序号是用来保证可靠性的--确认应答。(序号是给对方确认的,确认序号是对方给我确认的)

超时重传机制

主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;
但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;
因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉.
这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果.

那么, 如果超时的时间如何确定?

  • 最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回";
  • 但是这个时间的长短, 随着网络环境的不同, 是有差异的;
  • 如果超时时间设的太长, 会影响整体的重传效率;
  • 如果超时时间设的太短, 有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间

  • Linux(BSD UnixWindows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时;
  • 时间都是500ms的整数倍;
  • 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传;
  • 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增;
  • 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接;

连接管理机制(三次握手四次挥手)

在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接

服务端状态转化

[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接。
[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文。
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了。
[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT。
[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)。
[LAST_ACK -> CLOSED] 服务器收到了对FINACK, 彻底关闭连接

客户端状态转化

[CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段。
[SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据。
[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close, 向服务器发送结束报文段, 同时进入FIN_WAIT_1。
[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段。
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK。
[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态。

为什么是三次?

并且我们发现最后一次是没有应答的,即是否被对方收到是不一定的,所以三次称握手不一定成功。(建立连接不一定成功)(前两次可以保证,因为有应答)
比如如果是一次握手就可以,那就是说只要发送SYN就可能建立连接,那么这样是非常容易被攻击的。(就比如一直发送SYN(这种攻击方式被称为SYN洪水)(服务器资源被消耗导致正常的无法正常运行))

两次呢?

其实和一次是类似的,服务端只要发出去,那也就是认为连接建立好了,我们依旧发送大量SYN给服务端,然后服务器收到了再发一个报文,没有确认里面就建立了连接,同样会被消耗大量资源(要维护大量连接)。
前两次不行是因为每次都是让server端认为连接已经建立好了,而三次就不一样了,可以把确认连接的机会交给server端,因为只有三次握手最后一次握手成功了之后,你的server端来结束三次握手。客户端,三次握手当他最后的ACK发送出去的时候,他并不知道这个报文有没有给server端收到,所以第三次丢失是客户端最害怕的事情,但是这并不影响服务端!因为服务端只要三次握手了,服务端才是最后确认ACK的人。即正常的情况下客户端给服务端发送大量的SYN,我给你ACK那么你就必须得给我ACK然后我才认为连接建立好了,换句话说,只要我们服务端和客户端建立好了连接,只要服务端有了维护连接的结构体,那么客户端必须维护,所以你想要用一台机器给我发送大量的SYN攻击,我的服务器也会把你拉下水,都会建立相同连接数,所以单主机攻击是非常困难的,因为客户端的资源也在不断减少,但服务器的资源一般更多。(三次握手客户端最后一次发送ACK就认为连接建立好了,就要去建立结构维护,这样服务器就把最好一次报文丢失的成本嫁接给了客户端,因为客户端面对的群体比较小,出上一些闲置的/非法的连接并不影响,但是服务端出现问题问题就大了,这也是为什么是三次握手(基数次))

那是不是三次握手就不会收到SYN洪水呢?
其实不是,三次握手只不过是以最小的成本,较小的握手次数,来避免直接的SYN洪水攻击,但至少保证,不会随随便便一台机器就可以把服务端搞挂掉。
为什么三次握手第二个理由:
客户端是一发一收,服务端也是,这样就可以用最小的成本去验证全双工。
再往上增加次数,也没有意义,因为不能解决任何问题(安全问题并不是主要在握手时考虑),而三次刚刚好以最小的成本,做出了最大的意义。

四次挥手

谁发送FIN就说明谁向另一方断开连接了,此时就不能向对方发送数据了,但是可以确认。
上面这就是四次挥手。
至于为什么不是两次(两次FIN),原因就在于需要知道对方确认收到了你的消息,所以有两次ACK回应,所以是四次挥手。
对于服务器发送FIN和ACK为什么不一起是因为你想和我断开连接,不代表我也想和你断开连接,我有可能还有数据没发完。(但是巧合情况,刚好服务器回应你的时候也想和你断开连接,那便是三次挥手了(特殊情况)(也就是后面说的捎带应答))

看图:
只要是客户端发送FIN状态就立马变为FIN_WAIT_1(谁发谁变),服务端收到消息,回应ACK,一回应立马变为CLOSE_WAIT后面类推。(CLOSE_WAIT就是半关闭状态,说白了,就是没关可以进行发消息。主动断开连接的一方最终要变为TIME_WAIT状态,他的特点就是发送最后一个ACK后,理论上是可以释放连接资源,但是他会等待一段时间才进入CLOSED状态)(只有第四次发送ACK的时候,才是双方真正的关闭了IO能力,才是把连接真正的关掉了,前面是关闭通信能力)

下图是TCP状态转换的一个汇总

较粗的虚线表示服务端的状态变化情况;
较粗的实线表示客户端的状态变化情况;
CLOSED是一个假想的起始点, 不是真实状态;

理解TIME_WAIT状态

  • TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态。
  • 我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方, TIME_WAIT期间仍然不能再次监听同样的server端口。
  • MSLRFC1122中规定为两分钟,但是各操作系统的实现不同, Centos7上默认配置的值是60s。
  • 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值。
  • 规定TIME_WAIT的时间请读者参考UNP 2.7节。

为什么是TIME_WAIT的时间是2MSL

  • MSLTCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话。
  • 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的)。
  • 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)。

解决TIME_WAIT状态引起的bind失败的方法

serverTCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的:
服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求).
这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉), 就会产生大量TIME_WAIT连接.
由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多, 每个连接都会占用一个通信五元组(ip,源端口, 目的ip, 目的端口, 协议). 其中服务器的ip和端口和协议是固定的. 如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了, 就会出现问题.
使用setsockopt()设置socket描述符的 选项SO_REUSEADDR1, 表示允许创建端口号相同但IP地址不同的多个socket描述符

理解 CLOSE_WAIT 状态 

//要改

对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题

滑动窗口

刚才我们讨论了确认应答策略, 对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段.这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候。
既然这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值. 上图的窗口大小就是4000个字节(四个段)。
  • 发送前四个段的时候, 不需要等待任何ACK, 直接发送。
  • 收到第一个ACK, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推。
  • 操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉。
  • 窗口越大, 则网络的吞吐率就越高。

左边是已经发送并且已经应答的区域
中间写了
后面是还没发送的数据
左边的数据收到了就会向右滑动

那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论

情况一: 数据包已经抵达, ACK被丢了

这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认; 

情况二: 数据包就直接丢了

  • 当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样
  • 如果发送端主机连续三次收到了同样一个 "1001" 这样的应答, 就会将对应的数据 1001 - 2000 重新发送。
  • 这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中。

这种机制被称为 "高速重发控制"(也叫 "快重传") 。

流量控制 

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)
  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端;
  • 窗口大小字段越大, 说明网络的吞吐量越高;
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端;

接收端如何把窗口大小告诉发送端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息;
那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字节么?
实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M ;

拥塞控制 

解决网络出问题(软件问题)的情况

虽然TCP有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题。
因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的。
TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据。
  • 此处引入一个概念程为拥塞窗口
  • 发送开始的时候, 定义拥塞窗口大小为1;
  • 每次收到一个ACK应答, 拥塞窗口加1;
  • 每次发送数据包的时候, 拥塞窗口接收端主机反馈的窗口大小做比较, 较小的值作为实际发送的窗口;

像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速度非常快。

  • 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍。
  • 此处引入一个叫做慢启动的阈值
  • 当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

  • TCP开始启动的时候, 慢启动阈值等于窗口最大值;
  • 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案

延迟应答 

如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小
  • 假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;
  • 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
  • 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;
  • 如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;
一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率;
那么所有的包都可以延迟应答么? 肯定也不是;
  • 数量限制: 每隔N个包就应答一次;
  • 时间限制: 超过最大延迟时间就应答一次;
具体的数量和超时时间, 依操作系统不同也有差异; 一般N2, 超时时间取200ms;

捎带应答 

当我们接受消息返回ACK的同时给ACK带上数据,也就是说回复的时候带上我想发送的数据,这才是tcp通信的真相。

在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 "一发一收" . 意味着客户端给服务器说了 "How are you", 服务器也会给客户端回一个 "Fine, thank you";
那么这个时候ACK就可以搭顺风车, 和服务器回应的 "Fine, thank you" 一起回给客户端。

面向字节流

创建一个TCPsocket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;

硬件(网卡)接受数据的时候是可以通过硬件中断(有个中断向量表。每一个中断要被处理,是要有中断向量表的,用中断号做下标,搞的一个函数指针数组,然后用中断号下标去指向特定的方法就可以了)让OS知道有数据的,让OS调用拷贝函数(解包,向上交付)把数据加载到接受缓冲区(tcp)/内存。(就像键盘输入一样)然后应用程序可以read从接受缓冲区读取数据。

  • 调用write, 数据会先写入发送缓冲区中;
  • 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
  • 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
  • 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
  • 然后应用程序可以调用read从接收缓冲区拿数据;
  • 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工
由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
  • 100个字节数据时, 可以调用一次write100个字节, 也可以调用100write, 每次写一个字节;
  • 100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100;

粘包问题

明确报文和报文的边界

  • 首先要明确, 粘包问题中的 "" , 是指的应用层数据包
  • TCP的协议头中, 没有如同UDP一样的 "报文长度" 这样的字段, 但是有一个序号这样的字段;
  • 站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中;
  • 站在应用层的角度, 看到的只是一串连续的字节数据;
  • 那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包;
那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界
  • 对于定长的包, 保证每次都按固定大小读取即可; 例如上面的Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
  • 对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置;
  • 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可);

对于UDP协议来说, 是否也存在 "粘包问题" 呢?

  • 对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层. 就有很明确的数据边界。
  • 站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况。

关于listen的第二个参数

1. 直接telnet,不accept,连接可以建立(也就是说accept和我们的三次握手没有关系,不调用,底层也会自动三次握手成功,调用accept只是把底层已经三次握手建立的连接拿上来,经此而已)

我们此时listen第二个参数是设置的2
我们一直发送了四次,发现有一个State不一样,也就是说当前连接还没有完成

为什么呢? 

这次我们是没有去accept的,但是如果将来有可能服务器非常忙,来不及accept(一瞬间来了几百上千的连接)那么我们底层的连接就要在OS层面上进行排队。
(listen的第二个参数叫做底层的全连接长度,算法是:n+1,表示在不accept的情况下,你最多能维护多少个全连接,如果收到更多,那么tcp就不再进行三次握手,而是收到这个请求,已半连接的形式存在着(太久了就会销毁),等后续有新连接关闭退出了,我们再把这个连接建立成功)

2. 然后我们现在qiut关闭两个连接(客户端主动断开连接),而我们服务端是没有关闭他的(关闭他需要先accpt再close),所以我们服务端的连接状态就是CLOSE_WAIT。

所以我们知道了,如果我们的服务端不关闭对应的文件描述符,那么所处的状态就叫做CLOSE_WAIT状态。
然后我们再变一下,此时我们accept,但是还是不close,经过测试,我们服务端的连接状态还是CLOSE_WAIT。(所以说只要不close他,那么客户端断开连接之后,我们服务端所处的连接状态一直都是CLOSE_WAIT) 

3. 我们在服务器运行切保持连接的情况下,ctrl+c关闭服务端,这样使我们的服务端为断开连接的一方,我们也发现他的状态将由ESTABLISHED变为TIME_WAIT。

只要走到了TIME_WAIT,断开连接的一方就认为自己已经把四次挥手的工作做完了。 

然后又过了一段时间,我们去查看,就发现TIME_WAIT等待的时间到了之后,连接就释放掉了,这也代表这连接真正关闭。

PS:如果客户端服务端建立好连接了,如果CLOSE_WAITE一直存在的话,他也是消耗资源的,如果在未来我们去netstat的时候,发现服务器上挂了大量的CLOSE_WAITE,那么基本上只有一种可能性,那就是我们自己写的网络服务器,把文件描述符获取上来了,但是没有调用close,因为没有关,所以他的状态就没有办法主动的再进行四次挥手,所以最终出现了这个问题。(所以如果往后发现服务器非常卡,就可以netstat查看一下是不是服务器上挂了大量的CLOSE_WAITE这样状态的连接(办关闭状态,等待调用close)(挂连接是要消耗资源的)) 

TCP异常情况

进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同。
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset即 使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放。
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, QQ 断线之后, 也会定期尝试重新连接。
比如说两个主机正在通信,服务端突然进程终止了。在我们双方进行通信时,双方一个或两个终止了,网络也是文件,打开文件的生命周期是随进程的,所以进程终止时,OS会自动关闭我们之前所打开的文件,在网络这里就相当于会自动进行四次挥手(因为三次握手也是网络自动做的,是tcp做的,tcp也属于OS),所以进程终止时,我们是不用担心的,即连接建立了我们把机器重启或者关机是不会出现问题的(就比如我们打开的有网页,去关机的时候,他就会有提示,是否关闭重启,如果是,他就会关闭应用(说明关机OS是需要关闭打开的应用的,包括我们曾经打开的客户端或者服务器(涉及网络就要进行四次挥手)),如果否,他就是取消关机)。
服务器ctrl+c后(先断开),就发现状态变成了TIME_WAIT。

 

就是说在TIME_WAIT期间想要重启服务是不行的,必须要等一会,等netstat 查不到刚刚那个连接就可以重新启动了。(因为文件描述符的声明周期是随进程的,虽然我们代码里面没有close,但是我们进程ctrl+c了,OS在终止杀掉进程的时候,底层会自动实现握手过程。就好比我们之前写文件的时候,文件描述符没有关,但是进程退出之后,OS会帮你关一样的。)

现在有问题,我都已经完成了四次挥手,为什么还要有TIME_WAIT状态呢?
是因为最后一次ACK是否被对方收到是不确定的,所以就怕他丢了,这样服务器就只能以异常情况关闭连接。解决问题的方式就是让主动发起连接的一方,退出发送最后一个报文时,先不要直接变成CLOSE,先等一等,要让对方尽量收到ACK(如果丢了另一方就大概可能会进行超时重传FIN(这个就是需要控制好TIME_WAIT的等待时间),而使发送方重新发送ACK)。(这个时间一般就是2倍的MSL(最大传送时间)(至少保证一来一回即一个FIN一个ACK),MSL就是两端传送的最大时间) 

解决TIME_WAIT状态引起的bind失败的方法
TIME_WAIT状态时,连接虽然名存实亡,但他依旧还是存在,此时bind的时候就意味着要bind的ip和端口依旧被占用,所以bind失败,但是这是有问题的。看WPS
setsockopt()
可以用两个,也可以只要上面那个,就写在创建套接字后面

PS:accept并不参与三次握手,而是从底层拿取连接! 

TCP小结

可靠性:
校验和
序列号(按序到达)
确认应答
超时重发
连接管理
流量控制
拥塞控制
提高性能:
滑动窗口
快速重传
延迟应答
捎带应答
其他:
定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)

基于TCP应用层协议

HTTP HTTPS SSH Telnet FTP SMTP
当然, 也包括你自己写TCP程序时自定义的应用层协议;

TCP/UDP对比

我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP? TCPUDP之间的优点和缺点, 不能简单, 绝对的进行比较
  • TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
  • UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播;
归根结底, TCPUDP都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定。

最后的最后,创作不易,希望读者三连支持💖

赠人玫瑰,手有余香💖

相关文章:

传输层协议 TCP UDP

目录 协议前菜 端口号 ​编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…...

一点就分享系列(实践篇6——上篇)【迟到补发】Yolo-High_level系列算法开源项目融入V8 旨在研究和兼容使用【持续更新】

一点就分享系列(实践篇5-补更篇)[迟到补发]—Yolo系列算法开源项目融入V8旨在研究和兼容使用[持续更新] 题外话 去年我一直复读机式强调High-level在工业界已经饱和的情况,目的是呼吁更多人看准自己,不管是数字孪生交叉领域&#…...

buu RSA 1 (Crypto 第一页)

题目描述: 两个文件,都用记事本打开,记住用记事本打开 pub.key: -----BEGIN PUBLIC KEY----- MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMAzLFxkrkcYL2wch21CM2kQVFpY97 /AvKr1rzQczdAgMBAAE -----END PUBLIC KEY-----flag.enc: A柪YJ^ 柛x秥?y…...

Python 二分查找:bisect库的使用

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…...

性能优化之HBase性能调优

HBase是Hadoop生态系统中的一个组件,是一个分布式、面向列存储的内存型开源数据库,可以支持数百万列(MySQL4张表在HBase中对应1个表,4个列)、超过10亿行的数据存储。可用作:冷热数据分离HBase适合作为冷数据…...

图像金字塔,原理、实现及应用

什么是图像金字塔 图像金字塔是对图像的一种多尺度表达,将各个尺度的图像按照分辨率从小到大,依次从上到下排列,就会形成类似金字塔的结构,因此称为图像金字塔。 常见的图像金字塔有两类,一种是高斯金字塔&#xff0…...

08-Oracle游标管理(定义,打开、获取数据及关闭游标)

目标 1.确定何时需要显示游标2.声明、打开和关闭显示游标3.从显示游标中提取数据4.了解与游标有关的属性5.使用游标FOR循环检索游标中的数据6.在游标FOR循环的子查询中声明游标7.评估使用逻辑运算符结合在一起的布尔条件游标 1、在使用一个PL/SQL块来执行DML语句或只返回一行结…...

Python判断字符串是否包含特定子串的7种方法

目录1、使用 in 和 not in2、使用 find 方法3、使用 index 方法4、使用 count 方法5、通过魔法方法6、借助 operator7、使用正则匹配转自:https://cloud.tencent.com/developer/article/1699719我们经常会遇这样一个需求:判断字符串中是否包含某个关键词…...

aop实现接口访问频率限制

引言 项目开发中我们有时会用到一些第三方付费的接口,这些接口的每次调用都会产生一些费用,有时会有别有用心之人恶意调用我们的接口,造成经济损失;或者有时需要对一些执行时间比较长的的接口进行频率限制,这里我就简…...

Hive---窗口函数

Hive窗口函数 其他函数: Hive—Hive函数 文章目录Hive窗口函数开窗数据准备建表导入数据聚合函数window子句LAG(col,n,default_val) 往前第 n 行数据LEAD(col,n, default_val) 往后第 n 行数据ROW_NUMBER() 会根据顺序计算RANK() 排序相同时会重复,总数不会变DENSE…...

JavaSe第7次笔记

1. C语言里面,NULL是0地址。Java中null和0地址没关系。 2.数组可以做方法的返回值。 3.可以使用变量作为数组的个数开辟空间。 4.断言assert,需要设置。 5.排序:Arrays. sort(array); 6.查找: int index Arrays. binarySea…...

什么是 Service 以及描述下它的生命周期。Service 有哪些启动方法,有 什么区别,怎样停用 Service?

在 Service 的生命周期中,被回调的方法比 Activity 少一些,只有 onCreate, onStart, onDestroy, onBind 和 onUnbind。 通常有两种方式启动一个 Service,他们对 Service 生命周期的影响是不一样的。 1. 通过 startService Service 会经历 onCreate 到 onStart,然后处于运行…...

Redis部署

JAVA安装 mkdir /usr/local/javacd /usr/local/java/wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u13…...

AT32F437制作Bootloader然后实现Http OTA升级

首先创建一个AT32F437的工程,然后发现调试工程配置这里的型号和创建工程选的型号不一致,手动更改一下,使用PW Link下载程序的话还要配置一下pyocd.exe的路径。 打开drv_clk.c文件的调试功能看下系统时钟频率。 项目使用的是AT32F437VMT7芯片&…...

Springboot项目启动初始化数据缓存

1.从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解, PostConstruct和PreDestroy,这两个注解被用来修饰一个非静态的void()方法,被PostConstruct修饰的方法会在服务器加载Servlet的时候运…...

深度学习必备知识——模型数据集Yolo与Voc格式文件相互转化

在深度学习中,第一步要做的往往就是处理数据集,尤其是学习百度飞桨PaddlePaddle的小伙伴,数据集经常要用Voc格式的,比如性能突出的ppyolo等模型。所以学会数据集转化的本领是十分必要的。这篇博客就带你一起进行Yolo与Voc格式的相互转化&…...

数据、数据资源及数据资产管理的区别

整理不易,转发请注明出处,请勿直接剽窃! 点赞、关注、不迷路! 摘要:数据、数据资源、数据资产 数据、数据资源及数据资产的区别 举例 CRM系统建设完成后会有很多数据,这些数据就是原始数据,业务…...

标度不变性(scale invariance)与无标度(scale-free)概念辨析

文章目录标度标度种类名义标度序级标度等距标度比率标度常用标度方法不足标度不变性标度不变(Scale-invariant)曲线和自相似性(self-similarity)射影几何分形随机过程中的标度不变性标度不变的 Tweedie distribution普适性&#x…...

WMS仓库管理系统解决方案,实现仓库管理一体化

仓库是企业的核心环节,若没有对库存的合理控制和送货,将会造成成本的上升,服务品质的难以得到保证,进而降低企业的竞争能力。WMS仓库管理系统包括基本信息,标签,入库,上架,领料&…...

css常见定位、居中方案_css定位居中

一、 定位分类 1、静态定位 position:static;(默认,具备标准流条件) 2、相对定位 position:relative; 通过 top 或者 bottom 来设置 Y 轴位置 通过 left 或者 right 来设置 X 轴位置 特点: 相对定位不会脱离文档流相对于自…...

【微信小程序】-- 自定义组件 -- 创建与引用 样式(三十二)

💌 所属专栏:【微信小程序开发教程】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...

ArangoDB——AQL编辑器

AQL 编辑器 ArangoDB 的查询语言称为 AQL。AQL与关系数据库管理系统 (RDBMS)区别在于其更像一种编程语言,更自然地适合无模式模型,并使查询语言非常强大,同时保持易于读写。数据建模概念 数据库是集合的集合。集合存储记录,称为文…...

Lesson 9.1 集成学习的三大关键领域、Bagging 方法的基本思想和 RandomForestRegressor 的实现

文章目录一、 集成学习的三大关键领域二、Bagging 方法的基本思想三、RandomForestRegressor 的实现在开始学习之前,先导入我们需要的库,并查看库的版本。 import numpy as np import pandas as pd import sklearn import matplotlib as mlp import sea…...

basic1.0链码部署(基于test-network 环境ubuntu20.04腾讯云)

解决了官方示例指令需要科学上网才能运行的问题(通过手动下载二进制文件和拉取官方fabric-samples)。具体的将bootstrap.sh脚本解读了一遍 具体可以参照我的博客 fabric中bootstrap.sh到底帮助我们干了什么?(curl -sSL https://bi…...

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime? System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…...

【网络】http协议

🥁作者: 华丞臧. 📕​​​​专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...

Thread::interrupted() 什么意思? 如何中断线程?

1、答: Thread::interrupted() 是一个静态方法,用于判断当前线程是否被中断,并清除中断标志位。 具体来说,当一个线程被中断后,它的中断状态将被设置为 true。如果在接下来的某个时间点内调用了该线程的 interrupted…...

Oracle OCP 19c 考试(1Z0-083)中关于Oracle不完全恢复的考点(文末附录像)

欢迎试看博主的专著《MySQL 8.0运维与优化》 下面是Oracle 19c OCP考试(1Z0-083)中关于Oracle不完全恢复的题目: A database is configured in ARCHIVELOG mode A full RMAN backup exists but no control file backup to trace has been taken A media…...

一起来学习配置Combo接口吧!

Combo接口是一个光电复用的逻辑接口,一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系,两者不能同时工作(当激活其中一个接口时,另一个接口就自动处于禁用状态)&#xf…...

C++模拟实现红黑树

目录 介绍----什么是红黑树 甲鱼的臀部----规定 分析思考 绘图解析代码实现 节点部分 插入部分分步解析 ●父亲在祖父的左,叔叔在祖父的右: ●父亲在祖父的右,叔叔在祖父的左: 测试部分 整体代码 介绍----什么是红黑树 红…...

HTTPS协议之SSL/TLS详解(下)

目录 前言: SSL/TLS详解 HTTP协议传输安全性分析 对称加密 非对称加密 证书 小结: 前言: 在网络世界中,存在着运营商劫持和一些黑客的攻击。如果明文传输数据是很危险的操作,因为我们不清楚中间传输过程中就被哪…...

OLE对象是什么?为什么要在CAD图形中插入OLE对象?

OLE对象是什么?OLE对象的意思是指对象连接与嵌入。那为什么要在CAD图形中插入OLE对象?一般情况下,在CAD图形中插入OLE对象,是为了将不同应用程序的数据合并到一个文档中。本节内容小编就来给大家分享一下在CAD图形中插入OLE对象的…...

【微信小程序】-- 自定义组件 -- 数据、方法和属性(三十三)

💌 所属专栏:【微信小程序开发教程】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...

【Spring 深入学习】AOP的前世今生之代理模式

AOP的前世今生之代理模式1. 概述 什么是代理模式呢??? 在不修改原有代码 或是 无法修改原有代码的情况下,增强对象功能,替代原来的对象去完成功能,从而达成了拓展的目的。 先给大家看下 JavaScript中实现方…...

操作系统复试

2017软学 给出操作系统的定义,分别从资源管理,任务调度,用户接口等三个方面论述操作系统的职能 操作系统是位于硬件层之上、所有其他系统软件层之下的一个系统软件,使得管理系统中的各种软件和硬件资源得以充分利用,方…...

藏经阁(五)温湿度传感器 SHT3x-DIS 手册 解析

文章目录芯片特性芯片内部框图芯片引脚定义芯片温湿度范围芯片寄存器以及时序讲解信号转换公式芯片特性 湿度和温度传感器完全校准,线性化温度补偿数字输出供电电压范围宽,从2.4 V到5.5 VI2C接口通讯速度可达1MHz和两个用户可选地址典型精度 2% RH和 0.…...

PCB焊盘设计基本原则

SMT的组装质量与PCB焊盘设计有直接的关系,焊盘的大小比例十分重要。如果PCB焊盘设计正确,贴装时少量的歪斜可以再次回流焊纠正(称为自定位或自校正效应),相反,如果PCB焊盘设计不正确,即使贴装位置十分准确,…...

mysql锁分类大全

前言 为什么会出现锁 MySQL中的锁是为了保证并发操作的正确性和一致性而存在的。 当多个用户同时对同一份数据进行操作时,如果不加控制地进行读写操作,就可能导致数据不一致的问题。例如,当多个用户同时对同一行数据进行写操作时&#xff…...

推荐几款主流好用的远程终端连接管理软件

一、介绍 远程终端连接管理软件是管理服务器、虚拟机等远程计算机系统不可或缺的工具之一,它可以通过网络连接到另一台计算机,以执行命令、编辑文件或进行其他管理任务,下面我将为大家介绍几款主流好用的远程终端连接管理软件,并…...

描述性统计

参考文献 威廉 M 门登霍尔 《统计学》 文章目录定性数据的描述方法条形图饼图帕累托图定量数据点图茎叶图频数分布直方图MINITAB 工具在威廉《统计学》一书将统计学分为描述统计学和推断统计学,他们的定义分别如下:描述统计学:致力于数据集的…...

第十四届蓝桥杯三月真题刷题训练——第 7 天

目录 第 1 题:三角回文数 问题描述 答案提交 运行限制 代码: 第 2 题:数数 问题描述 答案提交 运行限制 代码: 第 3 题:倍数问题_同余定理_分情况讨论 题目描述 输入描述 输出描述 输入输出样例 运行限…...

剑指 Offer 57. 和为s的两个数字

一、题目 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。 示例 1: 输入:nums [2,7,11,15], target 9 输出:[2,7] 或者 [7…...

PDF转word在线转换方法!操作简单又高效

相信很多已经工作的人都知道,PDF文件格式的优点在于兼容性强、安全性高,而且查看和传输给他人都很方便。但是,这种格式的文件也有不太方便的地方,那就是不能对文件内容进行编辑和修改。对于许多人来说,如果想要编辑修改…...

Jquery项目中使用vue.js

大家在工作的情况中,可能会遇到之前的老项目采用jq书写,或者修改或者新增功能在jq中,原始jq的项目,代码可维护性很差,一个页面几千行jq,可维护性很差,工作量巨大,所以这个时候大家可以引入vue.js。 第一步:引入vue.js…...

蓝桥杯 删除字符

题目描述 给定一个单词,请问在单词中删除 t 个字母后,能得到的字典序最小的单词是什么? 输入描述 输入的第一行包含一个单词,由大写英文字母组成。 第二行包含一个正整数 t。 其中,单词长度不超过 100&#xff0c…...

析构函数 对象数组 对象指针

🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章 🔥座右铭:“不要等到什么都没有了,才下定决心去做” &#x1…...

Vue对Axios网络请求进行封装

一、为什么要对网络请求进行封装? 因为网络请求的使用率实在是太高了,我们有的时候为了程序的一个可维护性,会把同样的东西放在一起,后期找起来会很方便,这就是封装的主要意义。 二、如何进行封装? 1、将…...

Android framework HAL(HIDL)

简述 当你在Android系统中使用不同的硬件设备(例如摄像头、传感器、音频设备等)时,你需要与硬件抽象层(HAL)进行通信。 HAL是一个中间层,它充当了硬件和应用程序之间的桥梁。但是,由于硬件设备…...

QML 模型(ListModel)

LIstModel(列表模型) ListModel 是ListElement定义的简单容器,每个定义都包含数据角色。内容可以在 QML 中动态定义或显式定义。 属性: count模型中数据条目的数量dynamic动态角色,默认情况下,角色的类型…...

你还在调戏AI,有的公司已经用ChatGPT开展业务了

近日,OpenAI 正式宣布开放 ChatGPT 和 Whisper 两个模型的 API,API 版本的ChatGPT 不仅功能更多、性能更强,而且还更便宜一一相当于目前 GPT-3 模型价格打一折!划重点OpenAl正式开放 ChatGPT 和 Whisper 模型的 API,目前 SnapChat…...