
负责数据能够从发送端传输接收端.
端⼝号(Port)标识了⼀个主机上进⾏通信的不同的应⽤程序;

在TCP/IP协议中,⽤"源IP","源端⼝号","⽬的IP","⽬的端⼝号","协议号"这样⼀个五元组来标识⼀个 通信(可以通过netstat-n查看);

端⼝号范围划分
有些服务器是⾮常常用的,为了使用⽅便,⼈们约定⼀些常⽤的服务器,都是⽤以下这些固定的端⼝号: • ssh服务器,使⽤22端口
• ftp服务器,使⽤21端口
• telnet服务器,使⽤23端口
• http服务器,使⽤80端口
• https服务器,使⽤443
我们⾃⼰写⼀个程序使⽤端⼝号时,要避开这些知名端⼝号. 两个问题
1. ⼀个进程是否可以bind多个端口号?
2. ⼀个端⼝号是否可以被多个进程bind?
UDP/IP/TCP的报头是二进制的
http的报头是文本格式的
UDP协议端格式


长度: 整个数据报(报头 + 载荷)的长度 0-65535 ,最大是64kb,报头长度是8字节(一个2字节)
校验和:CRC

但是这样的校验方法是有一点小问题的,有可能校验和一样,但是数据变了,前面的bit小一点,后面的大一点,让他的值一样

注意:但是这样发生的机率比较小
UDP传输的过程类似于寄信.
• ⽆连接:知道对端的IP和端⼝号就直接进⾏传输,不需要建⽴连接;
• 不可靠:没有确认机制,没有重传机制;如果因为⽹络故障该段⽆法发到对⽅,UDP协议层也不会给应 ⽤层返回任何错误信息;
• ⾯向数据报:不能够灵活的控制读写数据的次数和数量;
应⽤层交给UDP多⻓的报文,UDP原样发送,既不会拆分,也不会合并;
用UDP传输100个字节的数据:
• 如果发送端调⽤⼀次sendto,发送100个字节,那么接收端也必须调⽤对应的⼀次recvfrom,接收100 个字节;而不能循环调用10次recvfrom,每次接收10个字节
我们注意到,UDP协议⾸部中有⼀个16位的最大长度.也就是说⼀个UDP能传输的数据最大长度是 64K(包含UDP⾸部). 然而64K在当今的互联⽹环境下,是⼀个非常小的数字. 如果我们需要传输的数据超过64K,就需要在应⽤层⼿动的分包,多次发送,并在接收端⼿动拼装;
基于UDP的应⽤层协议:
• NFS:网络文件系统
• TFTP:简单⽂件传输协议
• DHCP:动态主机配置协议
• BOOTP:启动协议(⽤于⽆盘设备启动)
• DNS:域名解析协议 当然,也包括你⾃⼰写UDP程序时⾃定义的应⽤层协议;
TCP全称为"传输控制协议(TransmissionControlProtocol").⼈如其名,要对数据的传输进⾏⼀个详细 的控制;

16 位源端口号 / 目的端口号:区分同一设备上的不同应用程序(比如浏览器用 80 端口,微信用其他端口),源端口是发送方应用的端口,目的端口是接收方应用的端口。
32 位序号:TCP 是 “字节流” 协议,这个序号代表本报文段中第一个数据字节的编号,用来解决数据 “乱序” 问题,让接收方能按正确顺序拼接数据。
32 位确认序号:只有当 “ACK 控制位” 为 1 时有效,代表接收方期望收到的下一个字节的编号(相当于告诉发送方:“我已经收到了到这个编号 - 1 的数据”),是 “确认应答” 机制的核心字段。
4 位首部长度表示 TCP 报头的长度(单位是 4 字节),因为报头可能包含 “选项”,所以用这个字段确定 “报头结束、载荷开始” 的位置。
选项:可有可无,因此导致报头的长度是不固定的
注意:TCP的单位不是以字节为单位的,是以4字节位单位的
一行32个bit ,4个字节,除去选项,固定部分的就是4*5 = 20 个字节
保留位(6):TCP的设计者考虑到了UDP的长度不够,又不能扩展的问题,就再报头中设计了‘保留位’
◦URG (Urgent Pointer) - 紧急指针有效标志
◦ ACK (Acknowledgment) - 确认标志
◦ PSH (Push) :提⽰接收端应⽤程序立刻从TCP缓冲区把数据读⾛
◦RST (Reset) :对方要求重新建立连接;我们把携带RST标识的称为复位报⽂段
◦ SYN (Synchronize) :请求建立连接;我们把携带SYN标识的称为同步报⽂段
◦FIN (Finish):通知对方,本端要关闭了,我们称携带FIN标识的为结束报⽂段

六个标志位:TCP最核心的六个标识位
十六位校验和:检验数据是否出错误
16位校验和:发送端填充,CRC校验.接收端校验不通过,则认为数据有问题.此处的检验和不光包含 TCP⾸部,也包含TCP数据部分.
16位紧急指针:标识哪部分数据是紧急数据;
40字节头部选项:暂时忽略;

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

有序号就可以很好的应对“先发后至”的问题
引入序号后,就可以很好的根据序号对数据进行排序。
TCP需要处理后发先至的问题,确保应用程序,通过socket api读取到的数据顺序是正确的

TCP会安排收到这一端安排一个到“缓冲区”(操作系统内核,内存)
通过网卡读到这里的数据,先放到接收缓冲区中,后续代码里调用read ,也是从这里接受缓冲区来读的,序号没有排序完整,read 就阻塞,等待排序完整

超时重传


因此主机B会收到很多重复数据.那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉.
这时候我们可以利⽤前⾯提到的序列号,就可以很容易做到去重的效果.
• 最理想的情况下,找到⼀个最⼩的时间,保证"确认应答⼀定能在这个时间内返回". • 但是这个时间的⻓短,随着⽹络环境的不同,是有差异的.
• 如果超时时间设的太长,会影响整体的重传效率;
• 如果超时时间设的太短,有可能会频繁发送重复的包;
TCP为了保证无论在任何环境下都能⽐较⾼性能的通信,因此会动态计算这个最大超时时间.
• Linux中(BSDUnix和Windows也是如此),超时以500ms为⼀个单位进⾏控制,每次判定超时重发的 超时时间都是500ms的整数倍.
• 如果重发⼀次之后,仍然得不到应答,等待2*500ms后再进⾏重传.
• 如果仍然得不到应答,等待4*500ms进⾏重传.依次类推,以指数形式递增.
• 累计到⼀定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接.
当路由器 / 交换机处于繁忙状态时,待转发的数据量会超出其硬件支持的转发能力上限,类似 “马路路口的车流量超过了最大通行容量”。
优先丢弃新的数据:队列里的 “旧数据” 已经在排队等待处理—— 此时这些旧数据还没超过时效(如果已经过时,设备会主动丢弃队列里的过时包);而刚到达的 “最新数据” 如果被加入队列,需要等待前面所有旧数据处理完成才能轮到它,这个等待时间会远超数据包的时效,等处理完时它已经失效了。
即便网络丢包概率仅为 10%(90% 的数据可到达),也会严重影响实时应用(如《英雄联盟》等游戏)的体验,导致操作卡顿、画面延迟(即 “卡成 PPT”)。
当 A 向 B 传输数据发生超时后,TCP 会延长超时时间阈值;若后续持续超时,阈值会继续延长,但调整并非无限制:当超时次数或等待时间达到临界值,会判定网络出现严重故障,放弃本次传输。
TCP 重复数据去重机制的流程:
TCP 通过 “接收缓冲区 + 序号校验” 实现去重,流程为:
TCP可靠的原因是:超时重传和确认应答

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

从上面的图看,其实是四次,但是他这个B收到A传来的同步报文段,发送一个ack确认答应和sync 同步报文段其实就可以看作是一个
所以是“三次握手”

1. 投⽯问路,确认当前通信路径是否畅通.
2. 协商参数,通信双⽅共同确认⼀些通信中的必备参数数值
下图是TCP状态转换的⼀个汇总:

• 较粗的虚线表示服务端的状态变化情况;
• 较粗的实线表示客⼾端的状态变化情况;
• CLOSED是⼀个假想的起始点,不是真实状态
想⼀想,为什么是TIME_WAIT的时间是2MSL?
• MSL是TCP报⽂的最大⽣存时间,因此TIME_WAIT持续存在2MSL的话
• 就能保证在两个传输⽅向上的尚未被接收或迟到的报⽂段都已经消失(否则服务器⽴刻重启,可能会收到来自上⼀个进程的迟到的数据,但是这种数据很可能是错误的);
• 同时也是在理论上保证最后⼀个报文可靠到达(假设最后⼀个ACK丢失,那么服务器会再重发⼀个 FIN. 这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK);
⼀般而言,对于服务器上出现大量的CLOSE_WAIT状态,原因就是服务器没有正确的关闭socket,导致 四次挥⼿没有正确完成.这是⼀个BUG.只需要加上对应的close即可解决问题
1. 初始序号的核心定义
它是 TCP 连接建立阶段(三次握手)双方协商的关键信息,用于标记后续数据报文的起始编号,是 TCP 实现 “数据有序传输、去重、流量控制” 的基础。
2. 不从 0 开始的原因
3. 两次连接 ISN 差异大的原因
TCP 协议规定,ISN 需基于时间戳等动态参数生成(通常每 4 微秒递增 1),因此不同时间建立的连接,ISN 会随时间显著变化,既保证了序号的唯一性,也进一步强化了安全性与抗干扰能力。

四次挥手:有的时候可以合成一个,有的时候不行(就是延时应答)

正常四次挥手是主动关闭方(如客户端)与被动关闭方(如服务端)的分步骤关闭:
FIN:标记 “我没有数据要发了,准备关闭我的发送连接”;ACK:确认 “收到你的 FIN 请求”;FIN:标记 “我也没有数据要发了,准备关闭我的发送连接”;ACK:确认 “收到你的 FIN 请求”。当被动关闭方(服务端)收到 FIN 时,已经没有待发送的数据,此时服务端可以将 “步骤 2 的 ACK” 和 “步骤 3 的 FIN”** 合并为一个报文(FIN+ACK)** 发送,从而将四次挥手简化为三次:
FIN;FIN+ACK(合并了 ACK 和 FIN);ACK。当被动关闭方(服务端)收到 FIN 时,还有未发送的数据,此时必须执行 “延时应答” 逻辑:
ACK(立即确认客户端的 FIN,避免客户端超时重发);FIN:等待自己的剩余数据全部发送完毕、处理完成后,再发送 FIN。此时 “ACK” 和 “FIN” 无法合并(因为中间需要传输数据),所以必须保持四次挥手流程。
1. 两端角色
2. 步骤拆解(按流程顺序)
close(fd) 触发连接关闭,此时左侧进入 FIN_WAIT_1 状态;CLOSE_WAIT 状态;read(connfd, buf, size) 阻塞等待客户端的数据请求(直到 read 返回 0,才知道对方已停止发送数据)。FIN_WAIT_2 状态,等待右侧发送自己的 FIN 报文。read 返回 0(表示 “对方已无数据发送”),于是调用 close(connfd);LAST_ACK 状态。TIME_WAIT 状态(需等待2 倍 MSL(报文最大生存时间),防止最后一个 ACK 丢失导致右侧重发 FIN)。TIME_WAIT 超时后,进入 CLOSED 状态;CLOSED 状态。close 才会发送 FIN;TIME_WAIT状态需等待2 倍 MSL(即2*MSL),原因是: 例子必要性:(TIME_WAIT)

MSL(Maximum Segment Lifetime):最大的报文传送时间,默认是60s,可以修改
服务器(被动关闭方 B)因代码逻辑 bug,始终未调用close。
双方状态与行为
CLOSE-WAIT状态(但因未调用close,不会发送自己的 FIN);此时 B 会持续保存连接的相关信息,但无法进行正常数据通信,连接处于 “僵死” 状态。
刚才我们讨论了确认应答策略,对每⼀个发送的数据段,都要给⼀个ACK确认应答.收到ACK后再发送下 ⼀个数据段.这样做有⼀个⽐较⼤的缺点,就是性能较差.尤其是数据往返的时间较⻓的时候.

既然这样⼀发⼀收的⽅式性能较低,那么我们⼀次发送多条数据,就可以⼤⼤的提⾼性能(其实是将多个 段的等待时间重叠在⼀起了).

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

只有确认1001-2000收到了才会发送这个2001-3000,所以我提前收到了2001-3000,就可以直接确认收到了2001-3000

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

B接收:
TCP 接收方(这里是主机 B,即服务器)的 ACK “下一个是 3001”,只有在它确实收到了 “1~3000 的连续数据段” 时,才会发送。
如果接收方(B)没收到前面的段(比如 2001~3000 丢了),即使后面的段(3001~4000)先到达:
A接收:
接收方 B 已经完整收到了 1~3000 的连续数据段,也发了 “ACK=3001” 告诉 A“我收到 1~3000 了”,但这个 ACK 在发给 A 的过程中丢包了;之后 B 又收到了 A 发的 3001~4000,此时 A 可以直接从 4001 继续发送了但是 1~3000丢包了,A没有收到收到二零3001-4000 —— 因为 B 既然能处理 3001~4000,说明 B 肯定已经收到 2001~3000 了,所以A可以直接发送4001了,默认B收到2001-3000
在 TCP协议 中,ACK是累积的,这意味着每个ACK不仅表示某一块数据已经接收成功,还间接确认了前面所有的数据块已经接收到。所以,即便 ACK=3001 丢失了,主机A也能通过 主机B处理3001~4000的数据 来推测前面的数据已经被接收并确认。
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进⾏确认;

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

滑动窗口是默认机制,数据量决定了 “是否易产生重复 ACK”,进而影响重传方式 —— 超时重传是 “无重复 ACK 时的兜底”,快速重传是 “有重复 ACK 时的优化”,二者协同保障 TCP 的可靠传输。