📝学习感悟
最近学完 Linux 下 TCP 以及 HTTP 网络编程部分,对比前面 UDP 通信,最大感受就是 TCP 虽然可靠性高,但内部机制复杂,坑点也更多。UDP 只管发出去就结束,而 TCP 要处理连接建立断开、丢包重传、流量控制,还会出现粘包这种容易踩坑的问题。
1.TCP 协议
传输层TCP: 传输控制协议 (流式套接字)
1. TCP 特点
- 有连接;
- 面向字节流;
- 安全可靠的传输协议三次握手、四次挥手、应答机制、超时重传机制.... 等
- 机制复杂,实时性和效率没有 UDP 高
应用场景:HTTPS、MQTT、FTP
2. TCP 的三次握手和四次挥手机制
三次握手:TCP 建立连接时,通过三次握手,来确保通信双方都已经准备就绪。三次握手由客户端发起。
数据收发:
四次挥手:TCP 断开连接时,通过四次挥手,确保通信双方数据都已经收发结束。
3. TCP 编程
客户端流程:socket()-->connect()-->send()-->recv()-->close()
服务端流程:socket()-->bind()-->listen()-->accept()-->recv()-->send()-->close()
connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:请求建立连接参数:
sockfd:套接字addr:服务端的地址addrlen:地址长度返回值:- 成功:0
- 失败:-1
listen
int listen(int sockfd, int backlog);功能:监听客户端的三次握手参数:
sockfd:监听套接字backlog:最多允许监听的客户端的个数返回值:- 成功:0
- 失败:-1
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能:接收完成三次握手的客户端,并返回一个通讯套接字参数:
sockfd:监听套接字addr:保存接入的客户端的地址信息的指针addrlen:地址信息长度的指针返回值:- 成功:通讯套接字
- 失败:-1
recv
ssize_t recv(int sockfd, void *buf, size_t len, int flags);功能:接收网络数据参数:
sockfd:通讯套接字buf:存放接收到的数据的空间首地址len:期待接收到的字节数flags:0 默认方式返回值:- 成功:返回实际收到的字节数
- 失败:-1
返回 0 代表对方发送断开连接
4. TCP 报文头部
标志位:TCP 报文头部数据位:
SYN:请求建立连接标志位ACK:效应报文标志位PSH:携带数据的报文标志位FIN:请求断开连接标志位URG:紧急数据标志位RST:重置标志位
5. TCP 机制
- 三次握手机制:建立连接,确认双方收发能力正常
- 四次挥手机制:断开连接,保证双方数据全部处理完毕
- 应答机制:TCP 为发送的数据进行编号,发送数据时,报文头部的序列号是这包数据的第一个数据的编号;将来接收方需要给这包数据发送 ACK,ACK 报文中确认号是收到的最后一个字节编号 + 1;
- 超时重传机制:TCP 每发送一包数据后都要等待应答,如果超时时间之内没有收到应答,则重新发送这包数据。
- 滑动窗口机制:缓冲区,保存已发送并收到应答的数据、已发送未收到应答的数据、未发送但在对方处理范围内的数据。
- 延迟应答机制:TCP 可以发送多组数据,发送的同时等待应答。
- 流量控制机制:TCP 会根据发送端的数据处理和接收能力调整自己的发送速率,根据 ACK 中窗口值的大小进行动态调整流量。
- 捎带应答机制:ACK 可以和应用层发送的数据一起发出,表示对上包数据的响应。
6. TCP 粘包
粘包:发送端发送速度太快,接收端处理速度比较慢,导致数据在缓冲区缓存,应用层读出数据时,多包数据发生了粘连。
如何解决:
- 收发指定大小数据 (收发结构体)
注意:跨平台发送时平台的位数。
struct data { xxx; long num; }; send(sockfd, &data, sizeof(struct data), 0); recv(sockfd, &data, sizeof(struct data), 0);给发送的数据明显的分割符,应用层根据分隔符解析例:
hello\n、world\n,接收端按换行符分割解析。以自定义方式定义发送的数据帧格式,接收方严格按照协议方式解析帧格式示例:
帧头 数据长度 消息类型 校验 帧尾示例:5A 0101 1010 A5校验可选:8 位和校验、16 位和校验、CRC 校验。
2.HTTP 协议
应用层HTTP 协议:超文本传输协议基于传输层 TCP 协议,端口 80HTTPS:SSL 加密方式,默认端口 443
1. HTTP 协议工作流程
- 建立 TCP 连接
- 发送 HTTP 请求报文(URL)+ 正文
- 返回 HTTP 响应报文 + 正文
- 断开 TCP 连接
2. HTTP 报文
HTTP 有两类报文:
- 请求报文:从客户向服务器发送请求报文
- 响应报文:从服务器到客户的回答
HTTP 报文每一个字段都是 ASCII 码串,字段长度不确定。
http 请求报文示例
GET / HTTP/1.1\r\n Host: news.sohu.com\r\n User‑Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept‑Language: en‑US,en;q=0.5\r\n Connection: keep‑alive\r\n \r\nkeep‑alive:长连接,HTTP 通信结束,保持一段时间连接再断开close:短连接,HTTP 通信结束,立马断开连接
HTTP 响应报文示例
HTTP/1.1 200 OK\r\n Date: Wed, 02 Sep 2026 06:31:29 GMT\r\n Content‑Type: text/html;charset=utf‑8\r\n Server: openresty Vary: Accept‑Encoding Vary: Origin Vary: Accept‑Request‑Method Vary: Access‑Control‑Request‑Headers Set‑Cookie: SUV=1788330689823odinzX9W; Path=/; Domain=sohu.com; Max‑Age=946080000; Expires=Fri, 25 Aug 2056 06:31:29 GMT; Secure; SameSite=None Trace‑Id: a067a38f132249f8a173162c956fc468.88701.17883306898232510 Data‑Source: X‑Content‑Type‑Options: nosniff X‑XSS‑Protection: 0 S‑REQ‑ID: 12342245799539308391 S‑REQ‑TYPE: 0 X‑Cache‑Lookup: Cache Miss Content‑Encoding: gzip Cache‑Control: no‑cache Transfer‑Encoding: chunked X‑NWS‑LOG‑UUID: 12342245799539308391\r\n Connection: keep‑alive\r\n X‑Cache‑Lookup: Cache Miss\r\n \r\n <!DOCTYPE html><html lang=zh‑CN><head> <script>if(window&&window.performance&&typeof window.performance.now==='function'){3.HTTP 常用方法
| 方法 (操作) | 意义 |
|---|---|
| GET | 获取资源 |
| POST | 提交数据 |
| PUT | 在指明的 URL 下存储一个文档 |
| DELETE | 删除资源 |
4.HTTP 状态码
状态码都是三位数字,分为 5 大类:
1xx:通知信息,请求收到,正在进行处理2xx:成功,请求正常处理3xx:重定向,需要进一步操作完成请求4xx:客户端差错,请求语法错误 / 资源不存在5xx:服务器差错,服务器内部异常
常见状态码:
200 OK:请求成功202 Accepted:接受请求400 Bad Request:错误的请求404 Not Found:找不到资源
5.http 接口调用示例
plaintext
http://api.k780.com/?app=weather.today&cityNm=西安&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=jsonappkey:平台分配密钥sign:签名校验format=json:返回 json 格式数据
学习小结
- TCP 是面向连接可靠字节流协议,核心机制:三次握手、四次挥手、应答、超时重传、流量控制、滑动窗口;
- TCP 粘包是缓冲区现象,不是 bug,三种主流解决方案:固定结构体、分隔符、自定义帧协议;
- HTTP 基于 TCP,文本格式报文,区分长短连接,状态码区分请求结果;
- 实际开发嵌入式 Linux 网络编程,TCP 粘包是高频踩坑点,写代码的时候必须提前规划数据协议