AI大模型学习04:网络编程与多进程
以前你的 Python 程序都是自己跑自己的,像孤岛。今天,我们要让两台电脑上的程序互相发消息——这就是网络编程。
而且,为了让服务器能同时服务多个客户,我们还会引入“多进程”,就像开了好几个窗口同时接单。
一、先搞清三个最基本的概念:IP、端口、协议
想象你要给住在另一个城市的朋友写信:
-
IP 地址 = 朋友的小区地址(比如“幸福里小区3号楼”)。它帮你把信送到正确的那栋楼。
-
端口号 = 朋友家的门牌号(比如“203室”)。同一栋楼里有好多户,端口号告诉送信的人:“这封信是给 203 室的张三的”。
-
协议 = 你们俩约定好用中文写信,而不是英文或者火星文。协议规定了数据怎么包装、怎么确认收到、丢了怎么办。
所以网络编程的核心就是:用 IP 找到那台电脑,用端口找到那个程序,用协议保证两边能听懂。
小贴士
-
本机测试时,IP 可以用
127.0.0.1,意思是“我自己”。就像给自己打电话。 -
端口号一般用 1024 以上的数字(比如 8000、9000),因为低端口经常被系统程序占着。
二、TCP 协议:像打电话一样可靠
TCP 是互联网上最常用的协议之一,它的三大特点你一定要记住:
-
面向连接:正式说话之前,必须先“拨通电话”,双方确认对方在线。不像发短信(UDP),扔出去就不管了。
-
可靠传输:你发的每一个字节,对方都会确认收到。如果中途丢了,会自动重传,最后保证顺序不乱。适合网页、文件下载、聊天。
-
面向字节流:数据像流水一样连续发送,没有天然的“消息边界”。好比你把一桶水倒进管道,对方接到的是连续的水,你得自己想办法分成一杯一杯(比如加换行符、固定长度)。
1. 三次握手:怎么“拨通电话”的?
建立 TCP 连接时,双方要在底层交换三个小包,俗称三次握手。你可以想象成:
-
客户端:“喂,能听见吗?我想跟你说话。”(发 SYN)
-
服务器:“听见了,我也能听见你。你说吧。”(发 SYN+ACK)
-
客户端:“好的,那开始说正事。”(发 ACK)
为什么不是两次?
如果只有两次,服务器发出“听见了”之后,无法确认客户端是否真的准备好了。万一客户端的请求是重复的旧请求,服务器就直接连上了,可能浪费资源。三次握手能保证双方都同意、并且交换了初始序号,是最小的可靠方案。
2. 四次挥手:怎么“挂电话”的?
TCP 连接是全双工的,就是两边都能同时说话、同时听。所以关闭的时候,要分别关闭两个方向:
-
主动方:“我说完了,我要挂掉我这边的发送。”(发 FIN)
-
被动方:“好的,我知道你说完了。”(回 ACK)
此时被动方可能还有话没说完,所以它继续发。 -
被动方:“我也说完了,我要挂掉我这边的发送。”(发 FIN)
-
主动方:“好的,知道你挂了。”(回 ACK)
然后等一小会儿(TIME_WAIT),确保对方收到了最后的确认,才彻底关闭。
为什么不是三次?
因为被动方收到“我说完了”时,不一定立刻就能结束(可能还在发数据),所以不能把第二步和第三步合并。只有等它自己也发完了,才能单独发 FIN。这就成了四次。
你不需要背太深,但要知道:建立连接三次,断开连接四次,这是 TCP 的经典规矩。
三、Socket 是什么?一个“网络电话机”
操作系统提供了一个叫 Socket(套接字) 的东西,它就像一部电话机。你写 Python 程序,不需要自己处理网线、电信号,只需要:
-
买一部电话机(创建 socket 对象)
-
拨号或接听(connect、bind、listen、accept)
-
说话(send)
-
听对方说话(recv)
-
挂机(close)
我们来看服务端和客户端分别怎么“用电话”。
四、服务端:开一个“客服热线”
服务端程序要被动等待别人打进来。步骤依次是:
-
买电话机:创建一个 TCP socket。
-
插电话线:绑定一个 IP 和端口(
bind),告诉系统“我要在这个地址接电话”。如果 IP 写0.0.0.0,表示监听本机所有网卡;写127.0.0.1就只能本机自己打给自己。 -
设置成待机状态:调用
listen,告诉系统“可以有人排队了”。 -
接听一个来电:调用
accept,程序会卡住(阻塞),直到真的有客户端连上来。然后它会返回一个新的电话机(新的 socket),专门用于和这个客户聊天,而原来的电话机继续等下一个客户。 -
聊几句:用新 socket 的
recv接收对方的话(字节流,需要用decode转成字符串),用send发送回复(需要encode)。 -
挂断这个客户的电话:
close这个新 socket。如果还想服务下一个客户,就回到第 4 步继续accept。
注意:
accept和recv都是阻塞的,意思是程序会停在那行,直到有事件发生。这就引出了多任务的需求——如果服务端只用一个循环,一次只能服务一个客户,其他人得排队。
五、客户端:主动打电话的人
客户端就简单多了:
-
买电话机(创建 socket)。
-
拨号:
connect((服务器 IP, 端口)),阻塞直到接通。底层会完成三次握手。 -
说话/听话:
send和recv(和服务器端一样)。 -
挂机:
close。
客户端没有 bind、listen、accept,因为它是主动方。
六、顺便说一下:字符串和字节的转换
网络传输只认字节(bytes),不认字符串。所以:
-
发的时候:
"你好".encode('utf-8')变成字节串。 -
收的时候:收到的字节用
decode('utf-8')变回字符串。
两边的编码必须一致,一般都用 UTF-8。
七、多进程:让服务器能同时服务好几个人
刚才说了,如果服务端只有一个循环,那么一次只能跟一个客户聊天,其他人排队。这显然不实用。
解决办法是:当 accept 接到一个客户后,立刻创建一个子进程,让子进程去跟这个客户聊天,主进程继续回来 accept 等待下一个客户。这样就能“同时”服务很多人了。
1. 什么是进程?
进程就是程序的一次运行实例。你打开一个微信,操作系统就给微信分配一个进程。进程之间是隔离的,内存不共享(后面会讲到)。
2. 怎么创建子进程(Python 的 multiprocessing)
import multiprocessing
def 聊天(新socket, 客户地址):
# 这里写 recv/send 的循环
pass
if __name__ == '__main__':
监听socket = 创建并bind、listen...
while True:
新socket, 地址 = 监听socket.accept()
p = multiprocessing.Process(target=聊天, args=(新socket, 地址))
p.start() # 启动子进程
# 主进程继续回去 accept
重点:
-
target后面写函数名,不要加括号。 -
如果子进程要传参数,用
args=(参数1, 参数2),注意只有一个参数时也要写成(参数,)加个逗号。 -
在 Windows 上,创建进程的代码必须放在
if __name__ == '__main__':里面,否则会出错。
3. 进程 ID(PID)
每个进程都有一个编号,叫 PID。你可以用 os.getpid() 获取当前进程的 PID,os.getppid() 获取父进程的 PID。这对调试多任务很有用。
4. 进程之间默认不共享全局变量
这一点很重要:两个进程各自拥有一份全局变量的拷贝,你在子进程里修改了一个列表,主进程里看不到变化。因为它们的内存是独立的。如果非要共享,需要用队列、管道等专门工具(以后会学)。
八、一个小坑:端口被占用怎么办?
当你把服务器程序关掉后立刻重启,有时候会报“地址已被占用”。这是因为操作系统要等一会儿(TIME_WAIT 状态)才能释放端口。解决办法是在 bind 之前设置端口重用:
socket对象.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
记住:必须在 bind 之前,否则没用。
九、文件上传的思路(其实就是聊天模式的变种)
-
客户端:打开文件(二进制模式),循环读取一小块(比如 1024 字节),用
send发出去。发完关闭连接。 -
服务端:接受连接后,以二进制写模式打开一个新文件,循环
recv一小块,直接写入文件。当recv返回空字节(b'')时,表示客户端关闭了连接,上传完成。
为什么要分块?因为文件可能很大,一次性读入内存太浪费,网络也是流式的,边读边发更合理。
十、总结
| 概念 | 一句话解释 |
|---|---|
| IP | 找到哪台电脑 |
| 端口 | 找到电脑上的哪个程序 |
| TCP | 面向连接、可靠、字节流(像打电话) |
| 三次握手 | 建立连接时互相确认 |
| 四次挥手 | 关闭连接时分方向关闭 |
| Socket | 编程用的“网络电话机” |
| 服务端流程 | 创建→绑定→监听→accept→recv/send→close |
| 客户端流程 | 创建→connect→send/recv→close |
| 进程 | 程序的一次运行实例 |
| 多进程 | 用子进程同时处理多个客户端 |
| 全局变量 | 默认不共享 |
更多推荐

所有评论(0)