客户端代码,服务端代码如下
客户端代码
import socketSERVER_ADDR = '127.0.0.1'
PORT = 18000
CHUNK_SIZE = 1024 * 4def main():""" echo 客户端: 基于 AF_INET domain and TCP type 的 socket """# socket -> s.connect -> s.sendall -> s.shutdown -> loop s.recv -> s.closewith socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:s.connect((SERVER_ADDR, PORT))s.sendall(b"hello")s.shutdown(socket.SHUT_WR) # if without this line code, the server and client will be deadlocked!response = b""while data := s.recv(CHUNK_SIZE):response += dataprint(response.decode())if __name__ == "__main__":main()
服务端代码
import socketHOST = '0.0.0.0'
PORT = 18000
BACKLOG = 16
CHUNK_SIZE = 1024 * 4def handle_client(conn, addr):with conn:print(f"{addr[0]}:{addr[1]}", flush=True, end=' ')request = b""while data := conn.recv(CHUNK_SIZE):request += dataprint(request.decode(), flush=True)conn.sendall(request)def main():""" echo 服务器: 基于 AF_INET domain and TCP type 的 socket """# socket -> s.bind -> s.listen -> s.accept -> s.closewith socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind((HOST, PORT))s.listen(BACKLOG)print(f"Listening on {HOST}:{PORT}\n", flush=True) try:while True:conn, addr = s.accept()handle_client(conn, addr)except KeyboardInterrupt:print("\nServer shutting down...")if __name__ == "__main__":main()
运行结果如下
删除客户端一行代码就会发生死锁,如下
s.shutdown(socket.SHUT_WR) # if without this line code, the server and client will be deadlocked!
死锁发生的时候,按下Ctrl + C 结束客户端的运行,则有以下过程
- 客户端的内核开始清理客户端的socket资源,首先向服务端发送一个FIN报文
- 服务端内核收到FIN报文,然后服务端从recv返回,打印数据,然后sendall发送数据
- 客户端收到服务端发送的数据,不处理,向客户端发送RST报文,表示客户端的socket已经不存在,不要再发送数据了
- 服务端收到RST报文,关闭conn socket,重新回到main函数的while循环中