普通IO方式
服务端:
# -*- coding: utf-8 -*- """ @Time : 2021/6/19 10:12 @Author : xiaochao """ import socket # 创建一个socket # socket.AF_INET 类型指明为网络连接; # socket.SOCK_STREAM 表示当前连接为TCP连接 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定服务端,监听的端口 server_socket.bind(("localhost", 6999)) # 表示服务端,最多可接受5个等待的客户端连接;再多的连接请求会被拒绝 server_socket.listen(5) print("Start server") while True: # 等待某个客户端的连接,如果有多个客户端连接,后到的会排队 conn, addr = server_socket.accept() print("Success connect with:%s." % str(addr)) print(conn, addr) # 处理对当前客户端的连接请求 # 当前和客户端建立连接后,会一直在此处理或等待;直到对方关闭连接 while True: try: data = conn.recv(1024) print("receive:", data.decode()) msg = "hi client:%s, I am server, and I get you message." % str(addr) conn.send(msg.encode("utf8")) except (ConnectionResetError, ConnectionAbortedError) as e: print("当前连接已关闭,client:%s." % str(addr)) print(str(e)) break conn.close()
客户端:
# -*- coding: utf-8 -*- """ @Time : 2021/6/19 10:12 @Author : xiaochao """ import socket import time # 创建客户端socket client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 与服务端建立连接 client_socket.connect(("localhost", 7999)) print("Success connect with server") # 模拟发送10次消息 for i in range(10): time.sleep(5) msg = "hi server, I am client 1, message:%s." % i client_socket.send(msg.encode("utf8")) data = client_socket.recv(1024) print("Get server response:%s." % data.decode()) client_socket.close()
服务端多路复用:
# -*- coding: utf-8 -*- """ @Time : 2021/6/19 10:12 @Author : xiaochao """ import select import socket # 创建一个socket # socket.AF_INET 类型指明为网络连接; # socket.SOCK_STREAM 表示当前连接为TCP连接 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定服务端,监听的端口 server_socket.bind(("localhost", 7999)) # 设置非阻塞方式 server_socket.setblocking(False) server_socket.listen(5) print("Start server") inputs = [server_socket, ] while True: r_list, w_list, e_list = select.select(inputs, [], [], 3) for event in r_list: if event == server_socket: new_sock, addr = event.accept() print("Get new client:%s." % str(addr)) inputs.append(new_sock) else: client_name = event.getpeername() data = event.recv(1024) if data: print("Receive client:%s msg:%s." % (client_name, data.decode())) msg = "hi client:%s, i am server." % str(client_name) event.send(msg.encode("utf8")) else: print("Connect close with client:%s." % str(client_name)) inputs.remove(event)
创建两个客户端,连接多路复用的服务端,输出如下:
Start server Get new client:('127.0.0.1', 51550). Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:0.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:1.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:2.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:3.. Get new client:('127.0.0.1', 51555). Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:4.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:0.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:5.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:1.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:6.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:2.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:7.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:3.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:8.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:4.. Receive client:('127.0.0.1', 51550) msg:hi server, I am client 1, message:9.. Connect close with client:('127.0.0.1', 51550). Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:5.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:6.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:7.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:8.. Receive client:('127.0.0.1', 51555) msg:hi server, I am client 2, message:9.. Connect close with client:('127.0.0.1', 51555).
多路复用所带来的效果和好处,体现在以下两个方面:
1. 用一个线程,可以监控和处理多个文件描述符;
2. 将多个文件描述符的多次系统调用,优化为一次系统调用+内核层遍历文件描述符;
多路复用的文章参考: