第4章:传输层详解
大家好,我是你们的老朋友。今天我们来聊聊传输层,这一层可以说是TCP/IP协议栈里的「心脏」。我做了十几年网络,遇到过太多问题都出在这一层。说白了,传输层就是给上层应用提供「端到端」的通信服务。没有它,你的浏览器、、游戏,全都玩不转。
4.1 端口号概念
先说说端口号。很多人刚学网络时,会把IP地址比作「门牌号」,那端口号就是「房间号」。一台服务器只有一个IP,但可以同时跑Web服务、邮件服务、数据库服务。怎么区分?靠端口号。
端口号是一个16位的数字,范围0~65535。我习惯把它们分成三类:
- 知名端口(0~1023):HTTP用80,HTTPS用443,SSH用22。这些是固定的,别乱改。
- 注册端口(1024~49151):比如MySQL用3306,Redis用6379。应用开发者可以申请。
- 动态/私有端口(49152~65535):客户端临时用。你打开浏览器访问百度,系统会随机分配一个。
重要概念:一个TCP连接由四元组唯一标识——(源IP, 源端口, 目的IP, 目的端口)。这四个元素缺一不可。
我在项目中遇到过一个问题:两台服务器之间建立连接,明明IP和端口都对,就是连不上。查了半天,发现是源端口被防火墙限制了。嗯,端口号不只是「房间号」,它还是防火墙的「通行证」。
4.2 UDP协议特点与报文结构
UDP,全称用户数据报协议。我经常跟新人说:UDP就是个「莽夫」,它只管把数据扔出去,不管对方收没收到。
UDP的特点:
- 无连接:不需要三次握手,直接发数据。你想想看,这多快。
- 不可靠:不保证数据到达,不保证顺序,不保证不重复。
- 面向报文:应用层给多少数据,UDP就原样封装发送,不拆分也不合并。
- 头部开销小:只有8字节。
UDP报文结构:
| 字段 | 长度 | 说明 |
|---|---|---|
| 源端口 | 16位 | 发送方端口号 |
| 目的端口 | 16位 | 接收方端口号 |
| 长度 | 16位 | UDP头部+数据的总长度 |
| 校验和 | 16位 | 检测数据是否损坏 |
我的经验:UDP适合实时性要求高的场景,比如视频通话、在线游戏。我曾经调过一个视频会议系统,用TCP时画面卡得不行,换成UDP后流畅多了。代价是偶尔花屏,但用户能接受。
4.3 TCP协议特点与报文结构
TCP和UDP正好相反。TCP是个「老管家」,做事一丝不苟。它要确保每一份数据都准确送达,顺序不乱,不丢不重。
TCP的特点:
- 面向连接:通信前先建立连接,通信后释放连接。
- 可靠传输:有确认、重传、排序机制。
- 面向字节流:应用层数据被看作一串字节流,TCP会按需分段。
- 全双工通信:双方可以同时收发数据。
- 流量控制与拥塞控制:后面会细讲。
TCP报文结构(重点):
| 字段 | 长度 | 说明 |
|---|---|---|
| 源端口 | 16位 | 发送方端口 |
| 目的端口 | 16位 | 接收方端口 |
| 序列号 | 32位 | 本报文段数据的第一个字节序号 |
| 确认号 | 32位 | 期望收到的下一个字节序号 |
| 数据偏移 | 4位 | 头部长度,单位是4字节 |
| 标志位 | 6位 | URG/ACK/PSH/RST/SYN/FIN |
| 窗口大小 | 16位 | 接收方还能接收多少数据 |
| 校验和 | 16位 | 检测头部和数据 |
| 紧急指针 | 16位 | URG=1时有效 |
注意:TCP头部最小20字节,最大60字节。因为有「选项」字段,可以扩展。我曾经见过一个奇葩问题,某个交换机把带时间戳选项的TCP包给丢了,排查了两天才找到原因。
4.4 三次握手与四次挥手
这是面试必考题,也是实际排障的利器。我建议你不仅要背,还要理解为什么这么设计。
三次握手(建立连接):
- 客户端发SYN:seq=x,进入SYN_SENT状态。
- 服务端回SYN+ACK:seq=y,ack=x+1,进入SYN_RCVD状态。
- 客户端发ACK:seq=x+1,ack=y+1,双方进入ESTABLISHED状态。
为什么会是三次?不是两次?我刚开始学的时候也困惑。其实很简单:两次握手无法防止「已失效的连接请求」到达服务器。你想想看,如果客户端发了个SYN,网络延迟了,客户端超时重发,结果第一个SYN又到了。两次握手的话,服务器就直接建立连接了,白白浪费资源。三次握手让服务器等客户端的确认,就能避免这个问题。
四次挥手(释放连接):
- 客户端发FIN:seq=u,进入FIN_WAIT_1。
- 服务端回ACK:ack=u+1,进入CLOSE_WAIT。客户端收到后进入FIN_WAIT_2。
- 服务端发FIN:seq=v,进入LAST_ACK。
- 客户端回ACK:ack=v+1,进入TIME_WAIT,等待2MSL后关闭。
避坑指南:我曾经在生产环境遇到大量TIME_WAIT连接,导致端口耗尽。原因是短连接太多,客户端主动关闭。解决方案:要么用长连接,要么调整tcp_tw_reuse参数。记住,TIME_WAIT是为了保证最后一个ACK能到达,防止旧连接干扰新连接。
4.5 滑动窗口与流量控制
滑动窗口是TCP实现可靠传输和流量控制的核心机制。说白了,就是接收方告诉发送方:「你慢点,我快吃不消了」。
工作原理:
- 接收方在ACK中携带自己的「窗口大小」。
- 发送方根据窗口大小,决定发送多少数据。
- 窗口是动态变化的,接收方处理完数据,窗口就变大。
我习惯把滑动窗口想象成一条流水线。接收方就是流水线末端的工人,他处理得快,就喊「再来一箱」;处理得慢,就喊「先停停」。发送方必须听他的。
我的经验:有一次调优一个文件传输系统,发现速度上不去。抓包一看,接收方的窗口经常变成0。原来是接收方的应用程序读取太慢,导致缓冲区满了。优化了应用层的读取逻辑,速度直接翻倍。
4.6 拥塞控制
流量控制是「点对点」的,拥塞控制是「全局」的。它要防止太多数据注入网络,导致路由器、交换机过载。
TCP拥塞控制有四个算法:
- 慢启动:刚开始拥塞窗口cwnd=1,每收到一个ACK,cwnd翻倍。指数增长,直到慢启动阈值ssthresh。
- 拥塞避免:超过ssthresh后,每收到一个ACK,cwnd增加1/cwnd。线性增长,比较温和。
- 快重传:收到3个重复ACK,立即重传丢失的报文,不等超时。
- 快恢复:快重传后,ssthresh设为cwnd/2,cwnd设为ssthresh+3,然后进入拥塞避免。
为什么会这样设计?你想想看,网络拥塞就像高速公路堵车。慢启动是刚上高速时慢慢加速,拥塞避免是到了限速后匀速行驶,快重传是发现前面有事故立刻刹车,快恢复是绕过事故后重新提速。
注意:拥塞控制和流量控制是两个不同的概念。流量控制是接收方管发送方,拥塞控制是网络管发送方。我曾经见过有人把这两个搞混,调了半天参数,问题根本没解决。
好了,传输层的内容就讲到这里。端口号是入口,UDP和TCP是两种截然不同的传输方式,三次握手和四次挥手是连接管理的核心,滑动窗口和拥塞控制保证了传输的效率和公平性。这些知识点在实际工作中天天都会用到,建议你多抓包看看,理解会更深刻。
可可精品资料库