select实现socket server多并发服务器端
# -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import socket import select import queue server = socket.socket() server.bind((‘127.0.0.1‘, 9999)) server.listen() server.setblocking(False) # 要设置为非阻塞 input_list = [server, ] # 本身也要检测 output_list = [] msg_dic = {} while True: stdinput, stdoutput, stderr = select.select(input_list, output_list, input_list) # 第一个input_list参数为要用的连接,ouput_list为可能返回的连接,第二个input_list为可能报错的连接 # stdinput为连接的地址, stdoutput为返回的连接, stderr为错误的连接 print(stdinput, stdoutput, stderr) try: for r in stdinput: if r is server: # 来了个新连接 conn, addr = server.accept() print(‘当前连接客户端:‘, addr) input_list.append(conn) msg_dic[conn] = queue.Queue() # 初始化一个队列,用来储存要返回给客户端的数据 else: data = r.recv(1024) print(‘收到数据‘, data) msg_dic[r].put(data) output_list.append(r) # 放入返回的连接队列里 except socket.error: print(‘客户端断开连接‘) break for w in stdoutput: # 要返回给客户端的连接列表 data_to_client = msg_dic[w].get() w.send(data_to_client) # 把数据发送给客户端 output_list.remove(w) # 确保下次循环的时候stdoutput不返回已经处理完的连接 for e in stderr: if e in output_list: output_list.remove(e) input_list.remove(e) server.close() del msg_dic[e]
客户端
# -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import socket client = socket.socket() client.connect((‘127.0.0.1‘, 9999)) while True: msg = input(‘>>>:‘).strip() if len(msg) ==0:continue client.send(msg.encode(‘utf-8‘)) data = client.recv(1024) print(data)
selector模块
selector模块可以使用select和epoll,它会根据所处的平台来选出最适合的I/O多路复用机制,在windows下为select,在linux下为epoll
通过selector模块实现单线程上万并发的socket server
服务器端
# -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import selectors import socket sel = selectors.DefaultSelector() def accept(sock, mask): conn, addr = sock.accept() print(‘当前连接客户端‘, addr) conn.setblocking(False) # 把连接设置为非阻塞 sel.register(conn, selectors.EVENT_READ, read) # 新连接注册read回调函数,如果新的连接发送数据就执行read函数 def read(conn, mask): data = conn.recv(1024) if data: print(‘收到数据:‘,data) conn.send(data) else: print(‘客户端关闭‘, conn) sel.unregister(conn) # 注销注册的事件 conn.close() server = socket.socket() server.bind((‘localhost‘, 9999)) server.listen() server.setblocking(False) sel.register(server, selectors.EVENT_READ, accept) # 注册一个事件,如果来了连接,就调用accept函数 # EVENT_READ,表示可读,值mask为1,EVENT_WRITE表示可写,值mask为2 while True: events = sel.select() # 调用epoll或select,默认阻塞,有活动的连接就返回活动的连接列表 for key, mask in events: callback = key.data # 回调函数,即accept函数 callback(key.fileobj, mask) # key.fileobj为文件句柄,即还没建立连接的socket实例 sel.close() # 最后要关闭,确保所有的资源被释放
客户端
# -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import socket import sys msg = [ b‘python‘, b‘php‘, b‘java‘, ] server_address = (‘127.0.0.1‘, 9999) socks = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(300)] print(‘connecting to %s port %s‘ % server_address) for s in socks: s.connect(server_address) for m in msg: for s in socks: print(‘%s: sending "%s"‘ % (s.getsockname(), m)) s.send(m) for s in socks: data = s.recv(1024) print(‘%s: received "%s"‘ % (s.getsockname(), data)) if not data: print(sys.stderr, ‘closing socket‘, s.getsockname())
服务器端运行结果
客户端运行结果
300个socket连接1秒左右就全结束了