大家好,我是你们总在排查 "接口超时" 的后端博主!前几天帮同事调一个 TCP 粘包问题,他盯着代码哀嚎:"这网络编程到底是啥啊?握手挥手的,比谈恋爱还复杂!"
其实我刚学的时候也一样 ------OSI 七层模型记成 "七层汉堡",TCP/UDP 分不清像 "短信和电话"。今天就用大白话 + 段子,把网络编程的核心知识点嚼碎了喂你,看完保证你下次跟产品聊 "接口延迟" 时,腰板都能挺得更直!
一、先搞懂:网络编程到底是干啥的?
你以为网络编程是 "写个微信发消息"?太天真了!
简单说,网络编程就是让两台(或 N 台)电脑能 "聊天" 的技术。比如你用 Postman 调接口,其实是你电脑(客户端)给服务器电脑发了个 "暗号";服务器看懂后,再把数据打包发回来 ------ 这整个 "发暗号→解暗号→回消息" 的过程,就是网络编程在干活。
后端 er 天天跟 "接口""网关""微服务调用" 打交道,本质上都是在玩网络编程的花样。要是这玩意儿没搞懂,排查 "超时 504""数据丢包" 时,就只能像无头苍蝇一样乱撞~
二、网络通信架构:别再死记 OSI 七层了!看快递系统就懂
网络编程,是指让计算机设备 可以通过网络 与其它设备 进行数据交互,它是互联网"万物互联"的基石
我们目前常用的各种程序,有非常非常多都是基于网络编程实现的,比如:
- 使用微信收发消息
- 打开浏览器可以浏览各种网站
- 使用软件观看视频
- 各种直播平台
- ......
聊网络编程绕不开 "架构",但 OSI 七层模型(物理层→应用层)记不住咋办?别急,咱们拿 "快递系统" 类比,秒懂!
目前常用的是TCP/IP 四层架构(比 OSI 更实用,后端天天见),对应快递流程是这样的:
TCP/IP 四层 | 作用(快递版) | 后端 er 常接触的东西 |
---|---|---|
应用层 | 你写的快递单(比如 HTTP 请求里的 "要查用户信息") | HTTP、HTTPS、FTP、接口参数 |
传输层 | 快递员(负责把包裹送对人,还能选 "加急件") | TCP(慢但稳)、UDP(快但糙) |
网络层 | 快递中转站(选路线,比如从北京到上海走空运还是陆运) | IP 地址、路由器 |
网络接口层 | 快递车 / 飞机(负责实际运输,比如网线、WiFi) | 网卡、网线、WiFi 信号 |
举个例子:你在掘金刷文章时,流程是这样的 ------
- 应用层:浏览器帮你写好 "要访问掘金某篇文章" 的 HTTP 请求(快递单);
- 传输层:用 TCP 协议(选 "加急且保准到" 的快递员)把请求打包;
- 网络层:给包裹贴个 "掘金服务器 IP 地址"(中转站选路线);
- 网络接口层:通过 WiFi 把包裹发出去,最终到掘金服务器手里。
是不是比死记 "七层模型" 好懂多了?
三、网络通信三要素:少一个,你的接口就调不通!
就像寄快递必须有 "收件人地址 + 电话 + 快递公司",两台电脑要聊天,也得有三个核心要素,少一个都白搭:
1. IP 地址:电脑的 "家庭住址"
IP 就是电脑在网络里的唯一标识,比如你家的门牌号。没有 IP,数据就不知道该发给谁。
IPv4与IPv6
IPv4
IPv4由32个比特位(4个字节)组成,为了方便阅读采用了"点分十进制表示法",将4个字节转换成 .
分隔的十进制数字。如下图:

IPv6
随着时代的发展,需要联网的设备越来越多,而IPv4只有2的32次方个地址:IP地址不够用了
所以目前推出了IPv6:
- 采用128位二进制数据来表示(16个字节),号称可以为地球上的每一粒沙子编一个IP地址
- 每16位编成一组,每组采用十六进制数据表示,然后用冒号隔开(称为冒分十六进制表示法),如下图所示

后端 er 常遇到的:
- 公网 IP:比如服务器的47.xxx.xxx.xxx,相当于 "公司地址",全互联网都能找到它。
吐槽一句:以前调本地接口时,把127.0.0.1写成192.168.1.100(室友的 IP),结果查了半小时为啥调不通... 谁懂啊!
2. 端口号:电脑的 "房间号"
光有 IP 还不够 ------ 比如你家有 3 个房间,快递员得知道把包裹放哪个房间。端口号就是电脑里 "应用程序的专属房间号",范围是 0-65535。
什么是端口:端口是计算机设备内的程序 与 外界互联网通信时,数据的逻辑出入口。是16bit的二进制数据,范围是0~65535
端口的特点:端口是抢占式资源。
- 一台计算机设备总共有65536个端口,任意一个端口都是由应用程序抢占的,谁先抢到谁使用;
- 假如QQ抢先占用了8888端口,那么其它程序就不能再使用8888端口
端口的分类:
- 周知端口:0~1023,预留给操作系统或者知名应用程序占用的,比如HTTP占80端口,FTP占21端口等
- 注册端口:1024~49151,分配给用户级应用程序占用,比如MySQL数据库占3306,Redis缓存占6379等等
- 动态端口:49152~65535,客户端临时使用,用于建立连接后临时分配,如浏览器访问网页时临时生成的端口
注意:
- 0-1023 是 "系统专用端口",比如 HTTP 默认 80,HTTPS 默认 443,别乱占用;
- 后端写服务时,一般用 1024 以上的端口,比如 SpringBoot 默认 8080(但线上要改,避免被扫)。
3. 协议:电脑聊天的 "语言"
你跟快递员说 "寄到北京",他能听懂;但你跟外星人说,他就懵了 ------ 协议就是电脑之间的 "共同语言"。
常用的协议:
- HTTP:浏览器和服务器聊天用(比如刷掘金、查百度);
- TCP:可靠传输(比如转账、发消息,不能丢数据);
- UDP:快速传输(比如直播、游戏,丢点数据不影响)。
四、TCP 和 UDP:一个像打电话,一个像发朋友圈
后端 er 最常搞混的就是 TCP 和 UDP,其实一句话就能区分:TCP 是 "可靠但慢",UDP 是 "快但不可靠" 。再给你上两个生动类比:
1. UDP:发朋友圈式通信
- 特点:发完就跑,不管对方收没收到;没有连接,速度快。
- 类比:你发了条朋友圈,不管朋友看没看,你都不管了;可能有的朋友没刷到(丢数据),但你发得很快。
- 用在哪:直播(丢几帧画面不影响)、游戏(延迟比丢包重要)、DNS 查询(快就行)。
2. TCP:打电话式通信
- 特点:必须先 "接通",再聊天;保证数据不丢、不乱序;速度慢。
- 类比:你给朋友打电话,必须等对方 "喂" 了(接通),你才说话;说话时对方会 "嗯"(确认收到),不会丢话;但比发朋友圈慢。
- 用在哪:转账(不能丢数据)、发消息(不能乱序)、HTTP 请求(必须收到响应)。
重点:TCP 的三次握手和四次挥手
这俩是面试高频题,别再死记 "SYN、ACK" 了,看约会和分手的类比:
(1)三次握手:建立连接(像约会前确认)
目的:确保双方 "能发消息、能收消息",避免无效连接。
- 客户端(你):"晚上一起吃饭不?"(发 SYN,请求连接);
- 服务器(朋友):"好啊!那晚上 6 点?"(发 SYN+ACK,同意连接 + 确认收到);
- 客户端(你):"没问题,6 点见!"(发 ACK,确认收到朋友的回复)。

为啥要三次?如果两次的话,朋友没收到你最后一句 "没问题",还以为你反悔了,白等你 ------ 三次才能确保双方都没问题。
(2)四次挥手:断开连接(像分手后告别)
目的:确保双方都把数据发完了,再断开,避免丢数据。
- 客户端(你):"我这边说完了,要挂了啊"(发 FIN,请求断开);
- 服务器(朋友):"好,我知道你要挂了,我再看看还有没没说的"(发 ACK,确认收到请求,此时服务器可能还在发数据);
- 服务器(朋友):"我这边也说完了,挂吧"(发 FIN,告诉客户端我也发完了);
- 客户端(你):"好,那挂了,再见"(发 ACK,确认收到,等一会儿再断开,防止服务器没收到)。
为啥要四次?因为服务器收到断开请求后,可能还有数据没发完,得先 "确认收到",等数据发完了再 "主动说断开"------ 两次搞不定!
五、TCP 通信入门:用 Python 写个 "客户端 - 服务器" 聊天
光说不练假把式,咱们用 Python 写个最简单的 TCP 通信 demo,感受下 "握手 - 发消息 - 挥手" 的过程。
1. 服务器端(接收消息)
python
import socket
# 1. 创建TCP socket对象(买个电话)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定IP和端口(给电话装个号码)
server_addr = ('127.0.0.1', 8888) # 本地IP+8888端口
server_socket.bind(server_addr)
# 3. 监听连接(开机等电话)
server_socket.listen(5) # 最多同时接5个连接
print("服务器已开机,等客户端打电话...")
# 4. accept():等待并接受连接(接电话,三次握手就在这一步)
client_socket, client_addr = server_socket.accept()
print(f"接到来自{client_addr}的电话啦!")
# 5. 接收客户端消息(听对方说话)
data = client_socket.recv(1024) # 一次最多收1024字节
print(f"客户端说:{data.decode('utf-8')}")
# 6. 给客户端回消息(说话)
client_socket.send("我是服务器,我收到你的消息啦!".encode('utf-8'))
# 7. 关闭连接(挂电话,四次挥手)
client_socket.close()
server_socket.close()
print("服务器关机啦!")
2. 客户端(发送消息)
python
import socket
# 1. 创建TCP socket对象(买个电话)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器(打电话,三次握手)
server_addr = ('127.0.0.1', 8888)
client_socket.connect(server_addr)
print("已拨通服务器电话!")
# 3. 给服务器发消息(说话)
client_socket.send("我是客户端,你好呀!".encode('utf-8'))
# 4. 接收服务器回复(听对方说话)
data = client_socket.recv(1024)
print(f"服务器说:{data.decode('utf-8')}")
# 5. 关闭连接(挂电话,四次挥手)
client_socket.close()
print("客户端挂电话啦!")
怎么玩?
- 先运行服务器代码,会显示 "等客户端打电话...";
- 再运行客户端代码,就能看到双方 "聊天" 了;
- 这就是最基础的 TCP 通信 ------ 先握手,再发消息,最后挥手。
六、TCP 多收多发:解决 "发得多、收不全" 的坑
上面的 demo 有个问题:如果客户端发的消息超过 1024 字节,服务器就收不全了!这就是 TCP 的 "粘包" 问题(其实是因为 TCP 是 "流传输",像水流一样,不知道哪里是开头结尾)。
怎么解决?给数据加 "头信息"------ 比如先告诉对方 "我要发 1000 字节",对方就知道要收 1000 字节才停。
改进后的服务器接收逻辑:
python
# 改进:多收多发,先收长度,再收数据
def recv_all(client_socket):
# 1. 先收4字节,代表数据长度(固定用4字节存长度)
len_data = client_socket.recv(4)
if not len_data:
return None
# 2. 把4字节转成整数(知道要收多少数据)
data_len = int.from_bytes(len_data, byteorder='big')
# 3. 循环收,直到收满data_len字节
recv_data = b''
while len(recv_data) < data_len:
recv_data += client_socket.recv(1024)
return recv_data.decode('utf-8')
# 用改进后的方法接收
data = recv_all(client_socket)
print(f"客户端说:{data}")
客户端发送逻辑也要对应改:
ini
# 改进:先发数据长度,再发数据
def send_all(client_socket, data):
# 1. 把数据转成字节
data_bytes = data.encode('utf-8')
# 2. 计算数据长度,转成4字节
len_bytes = len(data_bytes).to_bytes(4, byteorder='big')
# 3. 先发送长度,再发送数据
client_socket.send(len_bytes)
client_socket.send(data_bytes)
# 用改进后的方法发送(比如发个长消息)
long_data = "我是一个很长很长的消息...(此处省略1000字)"
send_all(client_socket, long_data)
这样不管发多长的消息,都能完整接收,再也不怕 "粘包" 了!
七、TCP 多线程改进:一个服务器同时聊多个客户端
上面的服务器有个致命问题:一次只能接一个客户端的电话,其他客户端得排队!就像你开奶茶店,只有一个店员,客人多了就会堵。
解决办法:多线程------ 给每个客户端分配一个 "专属店员"(线程),服务器就能同时聊多个客户端了。
改进后的多线程服务器:
python
import socket
import threading
# 处理单个客户端的逻辑(每个客户端一个线程)
def handle_client(client_socket, client_addr):
print(f"新客户端{client_addr}连接啦!")
while True:
# 接收客户端消息
data = client_socket.recv(1024)
if not data: # 客户端断开连接
print(f"客户端{client_addr}走了...")
break
print(f"{client_addr}说:{data.decode('utf-8')}")
# 回复客户端
reply = f"我收到啦:{data.decode('utf-8')}"
client_socket.send(reply.encode('utf-8'))
# 关闭连接
client_socket.close()
# 主服务器逻辑
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 8888))
server_socket.listen(5)
print("多线程服务器已开机,等客户端连接...")
while True:
# 接新客户端
client_socket, client_addr = server_socket.accept()
# 开新线程处理这个客户端
thread = threading.Thread(target=handle_client, args=(client_socket, client_addr))
thread.daemon = True # 主线程退出时,子线程也退出
thread.start()
if __name__ == "__main__":
main()
现在你可以同时开 3 个客户端连接服务器,每个客户端都能独立聊天 ------ 这就是后端 "并发处理" 的雏形,像微服务里的 "多线程处理请求" 就是这个思路!
八、BS 架构:你每天刷的掘金,就是 BS 架构!
最后聊聊 BS 架构 ------ 后端 er 天天跟它打交道,但可能不知道 "我写的接口,就是 BS 架构的一部分"。
什么是 BS 架构?
BS = Browser/Server(浏览器 / 服务器),简单说就是 "用浏览器访问服务器" 的架构。比如你刷掘金、用百度、登网易云音乐网页版,都是 BS 架构。
对应的还有 CS 架构(Client/Server,客户端 / 服务器),比如微信、QQ、网易云音乐 APP------ 需要装客户端才能用。
BS 架构的流程(以刷掘金为例):
- 你打开浏览器(客户端),输入掘金网址(juejin.cn);
- 浏览器发送 HTTP 请求(应用层),通过 TCP 协议(传输层)连接掘金服务器;
- 掘金服务器(后端)接收请求,查数据库、处理业务逻辑,生成 HTML 页面;
- 服务器把 HTML 页面通过 TCP 发回浏览器;
- 浏览器解析 HTML,显示出你看到的掘金页面(文章、评论、点赞按钮)。
后端 er 的工作,就是写 "服务器处理请求" 的代码 ------ 比如用户点赞时,你写的接口接收 "点赞请求",更新数据库,再返回 "点赞成功" 的响应。
最后:网络编程没那么难,多练多踩坑就会了
今天把网络编程的核心知识点(架构、三要素、TCP/UDP、BS 架构)都过了一遍,其实核心就是 "让电脑好好聊天"。后端 er 不用把每个细节都背下来,但一定要理解 "为什么 TCP 要三次握手""UDP 适合什么场景"------ 这些知识会帮你更快排查线上问题。
比如下次遇到 "接口超时",你就能想:是 TCP 连接没建立?还是 UDP 丢包了?或者是端口号写错了?
如果你有过 "排查网络问题到凌晨" 的经历,或者对某个知识点有疑问,欢迎在评论区留言 ------ 咱们一起吐槽,一起搞定网络编程!