网络编程原来这么好懂?TCP 三次握手像约会,UDP 像发朋友圈

大家好,我是你们总在排查 "接口超时" 的后端博主!前几天帮同事调一个 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 信号

举个例子:你在掘金刷文章时,流程是这样的 ------

  1. 应用层:浏览器帮你写好 "要访问掘金某篇文章" 的 HTTP 请求(快递单);
  1. 传输层:用 TCP 协议(选 "加急且保准到" 的快递员)把请求打包;
  1. 网络层:给包裹贴个 "掘金服务器 IP 地址"(中转站选路线);
  1. 网络接口层:通过 WiFi 把包裹发出去,最终到掘金服务器手里。

是不是比死记 "七层模型" 好懂多了?

三、网络通信三要素:少一个,你的接口就调不通!

就像寄快递必须有 "收件人地址 + 电话 + 快递公司",两台电脑要聊天,也得有三个核心要素,少一个都白搭:

1. IP 地址:电脑的 "家庭住址"

IP 就是电脑在网络里的唯一标识,比如你家的门牌号。没有 IP,数据就不知道该发给谁。

IPv4与IPv6

IPv4

IPv4由32个比特位(4个字节)组成,为了方便阅读采用了"点分十进制表示法",将4个字节转换成 .分隔的十进制数字。如下图:

IPv6

随着时代的发展,需要联网的设备越来越多,而IPv4只有2的32次方个地址:IP地址不够用了

所以目前推出了IPv6:

  • 采用128位二进制数据来表示(16个字节),号称可以为地球上的每一粒沙子编一个IP地址
  • 每16位编成一组,每组采用十六进制数据表示,然后用冒号隔开(称为冒分十六进制表示法),如下图所示

后端 er 常遇到的:

  • 局域网 IP:比如你本地开发的127.0.0.1localhost),相当于 "自己家客厅",只有自己能访问;
  • 公网 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)三次握手:建立连接(像约会前确认)

目的:确保双方 "能发消息、能收消息",避免无效连接。

  1. 客户端(你):"晚上一起吃饭不?"(发 SYN,请求连接);
  1. 服务器(朋友):"好啊!那晚上 6 点?"(发 SYN+ACK,同意连接 + 确认收到);
  1. 客户端(你):"没问题,6 点见!"(发 ACK,确认收到朋友的回复)。

为啥要三次?如果两次的话,朋友没收到你最后一句 "没问题",还以为你反悔了,白等你 ------ 三次才能确保双方都没问题。

(2)四次挥手:断开连接(像分手后告别)

目的:确保双方都把数据发完了,再断开,避免丢数据。

  1. 客户端(你):"我这边说完了,要挂了啊"(发 FIN,请求断开);
  1. 服务器(朋友):"好,我知道你要挂了,我再看看还有没没说的"(发 ACK,确认收到请求,此时服务器可能还在发数据);
  1. 服务器(朋友):"我这边也说完了,挂吧"(发 FIN,告诉客户端我也发完了);
  1. 客户端(你):"好,那挂了,再见"(发 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("客户端挂电话啦!")

怎么玩?

  1. 先运行服务器代码,会显示 "等客户端打电话...";
  1. 再运行客户端代码,就能看到双方 "聊天" 了;
  1. 这就是最基础的 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 架构的流程(以刷掘金为例):

  1. 你打开浏览器(客户端),输入掘金网址(juejin.cn);
  1. 浏览器发送 HTTP 请求(应用层),通过 TCP 协议(传输层)连接掘金服务器;
  1. 掘金服务器(后端)接收请求,查数据库、处理业务逻辑,生成 HTML 页面;
  1. 服务器把 HTML 页面通过 TCP 发回浏览器;
  1. 浏览器解析 HTML,显示出你看到的掘金页面(文章、评论、点赞按钮)。

后端 er 的工作,就是写 "服务器处理请求" 的代码 ------ 比如用户点赞时,你写的接口接收 "点赞请求",更新数据库,再返回 "点赞成功" 的响应。

最后:网络编程没那么难,多练多踩坑就会了

今天把网络编程的核心知识点(架构、三要素、TCP/UDP、BS 架构)都过了一遍,其实核心就是 "让电脑好好聊天"。后端 er 不用把每个细节都背下来,但一定要理解 "为什么 TCP 要三次握手""UDP 适合什么场景"------ 这些知识会帮你更快排查线上问题。

比如下次遇到 "接口超时",你就能想:是 TCP 连接没建立?还是 UDP 丢包了?或者是端口号写错了?

如果你有过 "排查网络问题到凌晨" 的经历,或者对某个知识点有疑问,欢迎在评论区留言 ------ 咱们一起吐槽,一起搞定网络编程!

相关推荐
CYRUS_STUDIO8 分钟前
Miniconda 全攻略:优雅管理你的 Python 环境
前端·后端·python
用户2986985301414 分钟前
如何使用 Spire.Doc 删除 Word 中的表格?
后端
blueblood17 分钟前
🗄️ JFinal 项目在 IntelliJ IDEA 中的 Modules 配置指南
java·后端
lovebugs32 分钟前
Kubernetes 实战:Java 应用配置与多环境管理
后端·面试·kubernetes
superlls34 分钟前
(计算机网络)TCP 三握中第三次 ACK 丢失会发生什么?
网络·网络协议·tcp/ip
合作小小程序员小小店1 小时前
挖漏洞三步走
python·网络协议·web安全·网络安全·安全威胁分析
赵得C1 小时前
Java 多线程环境下的全局变量缓存实践指南
java·开发语言·后端·spring·缓存
nightunderblackcat2 小时前
新手向:Python编写简易翻译工具
开发语言·python
打不过快跑2 小时前
YOLO 入门实战(二):用自定义数据训练你的第一个检测模型
人工智能·后端·python
敲代码的火锅2 小时前
基于pyroscope-go项目性能数据持续收集
后端·go