Python进阶 网络编程笔记-多进程

发布时间:2026/6/1 9:27:40

Python进阶 网络编程笔记-多进程 Python 进阶 网络编程与多进程一、网络编程在解决什么问题网络很多台电脑用网线、WiFi、路由器等连在一起按共同遵守的规则协议交换数据。网络编程写程序让运行在不同电脑上的两个进程之间能发数据、收数据。比喻IP 地址小区地址——确定是哪一栋楼哪一台设备。端口号门牌 / 窗口号——同一栋楼里有很多房间很多程序端口决定数据交给哪一个程序。协议两个人都说普通话——约定数据怎么打包、怎么确认、怎么重传双方对得上才能通信。一句话IP 找机器端口找程序协议定规矩。二、IP 地址与端口IP 地址在网络里大致唯一标识一台设备。同一局域网里常用形如192.168.x.x的形式本机自己和自己测可以用127.0.0.1回环地址。端口 / 端口号一台电脑上可以同时跑浏览器、微信、你写的 Python 程序。只凭 IP 只能找到电脑端口用来区分“这份数据交给哪个进程”。端口一般是065535的整数写程序时常用1024以上的端口避免和系统常用服务冲突。公有端口1-1023 动态端口:1024-65535本机小操作Windows在命令行用ipconfig可以看本机 IP用ping 某 IP 或域名可以粗测网络通不通ping 127.0.0.1测本机协议栈是否正常。三、TCP 协议1. TCP 的三大特点面向连接真正传业务数据之前必须先建立连接底层就是你下面要学的三次握手。就像打电话要先拨通再说话不是丢完就走。可靠传输有确认、重传、按序等机制尽量保证你发过去的字节流对方按顺序收到或发现错误、连接异常。适合文件、网页、聊天等“不能乱、不能大面积丢”的场景。面向字节流传送的是连续的字节流没有“消息边界”帮你分包应用层要自己约定“一条命令到哪算结束”例如换行、固定长度、先长度再内容等。和UDP对比帮助记忆本课代码以 TCP 为主UDP无连接、不保证可靠、发出去就不管像发短信TCP 像打电话更“啰嗦”但更稳。2. 三次握手建立连接时到底发生了什么三次握手指的是客户端和服务器之间连续交换三个带控制位的报文段把“我要连你”“我准备好了”“我也准备好了”说清楚并交换初始序号后面的可靠传输都依赖这些序号。可以按下面四列来记谁发起、发什么、对方收到后明白什么、这一步的意义。次序方向发送内容习惯说法含义白话第 1 次客户端 → 服务器SYN“我想建立连接这是我的初始序号seq。”第 2 次服务器 → 客户端SYN ACK“我收到了你的请求ACK我也发起我这一侧的连接SYN这是我的初始序号。”第 3 次客户端 → 服务器ACK“我收到了你的 SYN双方序号对齐可以开始传数据。”这一阶段你要抓住的重点为什么是“三次”而不是两次两次只能保证“一方知道另一方在线”没法可靠地统一双方的初始序号、也没法很好地处理旧的、重复的连接请求。三次是在工程上公认的最小可用做法既让双方都确认“对方愿意连、自己也愿意连”又把两边的起始序号对齐避免很多边角问题。面试时可以说防止历史连接请求的报文扰乱本次连接、并同步双方初始序列号。握手阶段还几乎不传业务数据主要是同步状态。真正send的业务数据是在握手完成之后。SYN 洪泛等攻击和 SYN 报文有关了解即可写应用时知道“连上之前必经历这三步”即可。作用:确认双方的发送与接收能力正常3. 四次挥手关闭连接时为什么往往是四步TCP 是全双工同一时间里A 可以给 B 发B 也可以给 A 发两个方向可以独立关闭。关闭时常见情况是一边先说自己“发完了”另一边可能还有数据没发完所以要分步确认不能像握手那样简单合并成三步。次序方向发送内容习惯说法含义白话第 1 次主动关闭方 → 被动方FIN“我这边的数据发完了我要关闭我这边的发送。”第 2 次被动方 → 主动关闭方ACK“我知道你发 FIN 了。”此时被动方还可以继续发数据给主动方。第 3 次被动方 → 主动关闭方FIN“我这边也发完了我也关闭发送。”第 4 次主动关闭方 → 被动方ACK“我知道你 FIN 了。”连接完全释放过程中还要处理最后的确认。这一阶段你要抓住的重点为什么是四次被动方收到对方的 FIN 时往往立刻回 ACK表示“我知道你关了”但自己可能还没发完数据所以要等自己这边也发完再单独发自己的 FIN。因此中间的 ACK 和最后的 FIN 通常不能合二为一就形成了四次挥手少数情况下第二步和第三步可以合并成“带 FIN 的 ACK”那是特例。全双工关的是“某一个方向上的发送/接收”不是一瞬间两个方向同时无声无息消失。主动关连接的一方在发完最后一个 ACK 之后会进入TIME_WAIT状态有一段时间——为了万一最后一个 ACK 丢了对方重传 FIN 时你还能补 ACK。初学知道“用来兜底丢包”即可。四、Socket 是什么Socket套接字是操作系统提供给你的编程接口你的 Python 程序通过它把“发网络数据、收网络数据”这些底层细节包装成几个函数调用。你用socket.socket(地址族, 类型)创建一个套接字对象。IPv4 TCP对应socket.AF_INET和socket.SOCK_STREAM。可以理解为有了一个能走 TCP、能选 IPv4 地址的通信端点。五、TCP 编程整体流程服务端在做什么、客户端在做什么先记角色服务端在自家电脑上开好端口等人连被动。客户端知道对方的 IP 和端口主动连过去主动。下面按推荐顺序把每一步讲清楚这是你必须能默画、能讲给别人听的。一服务端从“建套接字”到“能和一个人聊天”步骤函数这一步在干什么必须能口述1socket.socket(AF_INET, SOCK_STREAM)向系统申请一个TCP / IPv4套接字。还没有绑定到具体 IP 和端口。2bind((IP, 端口))声明我这个程序在这个 IP 的这个端口上“安家”。别人要连服务器就连这个地址。或0.0.0.0常表示监听本机所有网卡127.0.0.1只有本机程序能连。参数必须是二元组端口是整数。3listen(backlog)把套接字设为被动监听状态backlog表示排队等待你accept的连接最多能堆多少。之后才能在这一步的套接字上accept。4accept()阻塞直到有一个客户端完成三次握手连上来。返回(新套接字, 客户端地址)。关键从此刻起你有两个套接字角色——原来的继续负责“在门口接人”新的专门和当前这位客户端收发数据。5新套接字.recv(大小)从当前连接收最多这么长的字节返回bytes。阻塞直到收到数据或连接关闭。要对accept返回的那只套接字操作常写成conn.recv不要对监听套接字recv。6新套接字.send(字节数据)往当前连接发数据Python 3 里要发字符串需先.encode(utf-8)。同样用accept返回的那只套接字如conn.send。7新套接字.close()结束和这一位客户端的会话底层会走关闭流程。可选原监听套接字.close()若服务器程序要彻底退出、不再接任何客户再关监听套接字。很多练习里为了继续接下一单不关监听套接字。口诀帮助背顺序创建 → 绑定 → 监听 →接电话accept→听/说recv/send→ 挂机close。二客户端从“建套接字”到“连上服务器”步骤函数这一步在干什么1socket.socket(AF_INET, SOCK_STREAM)同样创建一个 TCP / IPv4 套接字。2connect((服务器IP, 端口))主动向对方的bind地址发起连接底层完成三次握手。成功后才算“线路通了”。通常会阻塞到连上或失败。3send/recv和服务端一样用这同一个套接字收发客户端没有“接听套接字”和“通话套接字”的拆分。4close()关闭连接。三小结服务端与客户端“长得不一样”的地方服务端多bind、listen、accept客户端用connect代替这三步里的“等人连”逻辑。服务端accept会多出一个新套接字客户端始终就一个套接字在和服务器说话。accept、connect、recv常常阻塞程序会停在这一行直到连上、直到收到数据或连接异常——写多任务时就会明白为什么要进程/线程。六、字符串与二进制encode / decode网络里传的是字节bytes不是 Python 里的str。发送你好.encode(utf-8)接收data.decode(utf-8)双方必须用同一种编码否则就会乱码。多数项目默认UTF-8。七、端口被占用端口重用现象服务器刚关立刻再运行提示地址已被占用和TIME_WAIT等有关。做法在bind之前调用tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)记住一定要在bind前否则起不到你想要的“立刻重启还能绑同一端口”的效果。八、循环accept一个接一个接待多个客户端while True: 新套接字, 地址 监听套接字.accept() # 用 新套接字 recv / send 新套接字.close()含义可以一个接一个地服务很多客户但同一时刻通常只有一个循环在执行若你在内层又用while跟一个人聊很久别人还是要等——这就是“单线程顺序服务”。要很多人同时传文件、同时聊天后面要用多进程、多线程或异步本课会先用多进程做入门。九、文件上传思路和聊天同一套 API服务器accept后用wb打开要保存的文件循环recv(一块大小)把收到的byteswrite进文件若recv得到空字节b长度为 0一般表示对方关连接了上传结束跳出循环。客户端rb读本地文件循环send一块发完close()。为什么要分块文件可能很大一次读进内存不现实网络也是流式的适合边读边发、边收边写。十、多任务并发和并行并发一段时间内多个任务在交替执行单核 CPU 快速切换看起来像同时。并行多核时不同任务真的在同一时刻各跑各的核心。多任务的目的让 CPU别闲着提高整体效率。和后面线程对比时先记一句进程之间数据默认不共享同进程里的线程共享内存以后会细讲锁。十一、多进程multiprocessing进程程序的一次运行实例操作系统给进程分配资源内存空间等。打开一个软件通常至少一个进程。1. 创建与启动importmultiprocessingdefjob():print(子进程里的代码)if__name____main__:pmultiprocessing.Process(targetjob)p.start()target写函数名不要写成job()加了括号就变成“先执行函数再把返回值当 target”了。start()才真正启动子进程。Windows 必记创建进程、写Process、start()的代码要放在if__name____main__:里面否则可能反复启动子进程或报错。2. 传参args和kwargsdefwork(name,n):...p1multiprocessing.Process(targetwork,args(张三,5))p2multiprocessing.Process(targetwork,kwargs{name:李四,n:10})args元组按位置传。只有一个参数时要写成(x,)不能写成(x)后者在 Python 里不是元组。kwargs字典键名必须和函数参数名一致。3. 查看 PIDimportosimportmultiprocessing os.getpid()# 当前进程号os.getppid()# 父进程号multiprocessing.current_process().pid进程结束后 PID 会被系统回收以后可能被别的进程复用在存活期内可以把它当成该进程的标志。4. 全局变量为什么不共享每个子进程里是父进程内存的一份拷贝模块级别的my_list []在不同进程里各有一份。一个进程里append另一个进程看不到。要在进程间传数据需要队列、Manager、管道等后面课程会接触先建立“默认不共享”的直觉。5. 守护进程daemonpmultiprocessing.Process(targetjob)p.daemonTrue# 要在 start() 之前设p.start()守护进程随主进程退出而结束适合“主程序关了后台辅助任务也不用留了”的场景默认则主进程会等子进程跑完再结束行为因写法略有差异以课堂演示为准。十二、容易写错的地方bind写成bind(127.0.0.1, 8080)——少了括号应该是bind((127.0.0.1, 8080))。端口写成字符串8080——应该是整数8080。用监听套接字去recv/send——应对accept返回的套接字操作。忘了encode/decode或两端编码不一致。SO_REUSEADDR写在bind后面。recv得到b仍死循环——要判断长度为 0表示对端关了。Windows 下if __name__ __main__:忘写。args(5)不是单元素元组应args(5,)。今日自测1. IP 地址的作用在网络中唯一标识一台设备让数据能到达“哪台电脑”。可记收货地址、楼栋。2. 端口号的作用在一台计算机上**标识正在运行的哪个程序进程**接收数据。IP 找机器端口找程序。3. TCP 的特点面向连接、可靠传输、面向字节流。可与 UDP 对比记忆。4. TCP 服务器端收发消息要走哪些步骤socket创建 →bind→listen→accept得到新套接字→recv/send在新套接字上→close新套接字监听套接字可继续接人。5. 什么是进程程序在操作系统里的一次运行实例是资源分配的基本单位。6. 怎么用multiprocessing起多进程import multiprocessingobj multiprocessing.Process(target函数名, args..., kwargs...)→obj.start()Windows 下代码放在if __name__ __main__:。7. 怎么获取 PIDos.getpid()、os.getppid()、multiprocessing.current_process().pid。8.args和kwargs怎么用args元组按位置传单元素写(x,)kwargs字典按关键字传键对齐参数名。9. 进程之间共享全局变量吗默认不共享各进程有独立拷贝要共享需专门机制。最小示例服务端单次接一个客户端、收一条再回一条importsocket serversocket.socket(socket.AF_INET,socket.SOCK_STREAM)server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)server.bind((0.0.0.0,9000))server.listen(128)conn,addrserver.accept()dataconn.recv(1024).decode(utf-8)print(收到:,data)conn.send(服务器已收到.encode(utf-8))conn.close()server.close()客户端importsocket csocket.socket(socket.AF_INET,socket.SOCK_STREAM)c.connect((127.0.0.1,9000))c.send(你好.encode(utf-8))print(c.recv(1024).decode(utf-8))c.close()先按注释理解顺序再改成你自己的 IP 和端口做练习。

相关新闻