当前位置: 首页 > news >正文

网络通信中的死锁

客户端代码

点击查看代码
import socket
from pathlib import PathSERVER_ADDRESS = Path("/tmp/uds_socket")
CHUNK_SIZE = 1024def main():# Create Unix Domain Socket in client sidewith socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:s.connect(str(SERVER_ADDRESS))s.sendall(b"Hello from client")response = b""while data := s.recv(CHUNK_SIZE):response += dataprint(response.decode())if __name__ == "__main__":main()

服务端代码

点击查看代码
import socket
from pathlib import PathSERVER_ADDRESS = Path("/tmp/uds_socket")
CHUNK_SIZE = 1024def main():# If the socket file already exists, remove itif SERVER_ADDRESS.exists():SERVER_ADDRESS.unlink()# Create Unix Domain Socket in server sidewith socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:s.bind(str(SERVER_ADDRESS))s.listen(16)print(f"[*] Listening at {str(SERVER_ADDRESS)}...")while True:try:conn, _ = s.accept()except KeyboardInterrupt:print("\n[*] Shutting down server...")breakwith conn:request = b""while data := conn.recv(CHUNK_SIZE):request += dataprint(f"[From client]: {request.decode()}")conn.sendall(b"Hello from server")conn.shutdown(socket.SHUT_WR)if __name__ == "__main__":main()

启动服务端,然后运行客户端就会出现死锁:客户端和服务端都阻塞在recv方法上,具体过程和解决办法分析如下

  1. 过程

启动服务端,服务端首先阻塞在accept上
启动客户端,客户端成功连接服务端,服务端从accept返回,然后阻塞在recv方法上
客户端使用sendall发送数据之后也阻塞在recv方法上,至此死锁就发生了!

  1. 解决办法

这是经典的死锁发生的必要条件之一:循环等待。
客户端在发送完数据之后应该将自己的状态设置为半关闭状态,然后客户端也会将自己的半关闭状态通知给客户端。
服务端的接收缓冲区为空,因此recv方法会一直阻塞,除非发生以下两种情况之一:

  • 客户端发送通知自己是半关闭状态
  • 客户端发送通知自己是全关闭状态

服务端收到客户端的半关闭或者全关闭通知之后,会从recv方法中返回,获得一个空字节串,然后结束while循环。
服务端使用sendall方法发送所有数据,然后进入半关闭状态,然后进入全关闭状态。
客户端recv方法收到数据并且知道了服务端处于半关闭或者全关闭状态,就会从recv方法中返回,获得一个空字节串,然后结束while循环。
解决办法:在客户端发送万数据之后,添加一段代码s.shutdown(socket.SHUT_WR)
在服务端代码中,shutdown方法不是必要的,因为服务端代码在sendall之后,即使没有使用shutdown进入半关闭状态,也会关闭自己的socket,进入全连接状态
但是,如果客户端/服务端确认自己不会再发送数据了,关闭自己的写端进入半关闭状态可以让代码更健壮。

修改后的客户端代码

点击查看代码
import socket
from pathlib import PathSERVER_ADDRESS = Path("/tmp/uds_socket")
CHUNK_SIZE = 1024def main():# Create Unix Domain Socket in client sidewith socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:s.connect(str(SERVER_ADDRESS))s.sendall(b"Hello from client")# TODO client.shutdown is very important, otherwise server won't get EOF and keep waiting for data# TODO then client and server will be deadlocked!!!s.shutdown(socket.SHUT_WR)  response = b""while data := s.recv(CHUNK_SIZE):response += dataprint(response.decode())if __name__ == "__main__":main()

运行结果

image

http://www.hskmm.com/?act=detail&tid=1046

相关文章:

  • java课前问题
  • CSP-S模拟19
  • union类型
  • PDE,广义特征问题,和神经特征函数法
  • 碳硫仪推荐品牌,是谁赢得用户口碑?
  • Python Flask框架入门_4.通过token认证验证API的访问权限(集成数据库连接池版本)
  • vue路由
  • 学习笔记
  • 学生开发者经验|豆包大模型 + TRAE,让 AI 应用快速落地
  • 绪论与Java基本语法课前问题
  • openssl编程之sm2密钥生成
  • 查看mysql具体使用那个glibc的版本的mysql
  • 【A】月半猫想吃麦当劳(待完坑)
  • 【A】宝宝肚肚打雷了(待完坑)
  • 01_TCP协议概念
  • 【A】杂题宣讲(待完坑)
  • 登录认证-上篇:基于 Session 的传统身份验证
  • 【A】chipi chipi chapa chapa
  • vLLM框架本地布署Qwen3-32B模型 - yi
  • 项目管理软件中有哪些不同的模块以及如何导出其报告?
  • Kubernetes命名空间(Namespace)
  • linux安装python
  • 【IEEE、电力学科品牌会议】第五届智能电力与系统国际学术会议(ICIPS 2025)
  • Vllm部署大模型
  • CE第9关X64版本问题记录
  • 题解:P14013 [POCamp 2023] 送钱 / The Generous Traveler
  • 第十三届 TCCT 随机系统与控制专题研讨会 暨2025年智能控制与计算科学国际学术会议 (ICICCS 2025)
  • 注释
  • Microsoft 推出 .NET 10 RC 1
  • 2025 第九届控制工程与先进算法国际论坛(IWCEAA 2025)