知识点:
如上所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
使用:
readable, writable, exceptional = select.select(inputs, outputs, inputs)
第一个参数是我们需要监听可读的套接字, 第二个参数是我们需要监听可写的套接字, 第三个参数使我们需要监听异常的套接字, 第四个则是时间限制设置.
如果监听的套接字满足了可读可写条件, 那么所返回的can,read 或是 can_write就会有值了, 然后我们就可以利用这些返回值进行随后的操作了。相比较unix 的select模型, 其select函数的返回值是一个整型, 用以判断是否执行成功
案例:
1)服务器
import socket,select # Function to broadcast chat messages to all connected clients def broadcast_data(sock, message): # Do not send the message to master socket and the client who has send us the message for socket in CONNECTION_LIST: if socket != server_socket and socket != sock: try: socket.send(message) except: # broken socket connection may be, chat client pressed ctrl+c for example socket.close() CONNECTION_LIST.remove(socket) if __name__ == "__main__": # List to keep track of socket descriptors CONNECTION_LIST = [] RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2 PORT = 22 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # this has no effect, why ? server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(("0.0.0.0", PORT)) server_socket.listen(10) # Add server socket to the list of readable connections CONNECTION_LIST.append(server_socket) print("Chat server started on port " + str(PORT)) while 1: # Get the list sockets which are ready to be read through select read_sockets, write_sockets, error_sockets = select.select(CONNECTION_LIST, [], []) for sock in read_sockets: # New connection if sock == server_socket: # Handle the case in which there is a new connection recieved through server_socket sockfd, addr = server_socket.accept() CONNECTION_LIST.append(sockfd) print("Client (%s, %s) connected" % (addr,sockfd)) #*****这里修改了服务器******** broadcast_data(sockfd,("[%s:%s] entered room " % (addr,sockfd)).encode('utf-8')) # Some incoming message from a client else: # Data recieved from client, process it try: # In Windows, sometimes when a TCP program closes abruptly, # a "Connection reset by peer" exception will be thrown data = sock.recv(RECV_BUFFER) if not data or data.decode('utf-8') == 'exit': server_socket.close() if data: broadcast_data(sock, (" " + '<' + str(sock.getpeername()) + '> ' + data.decode('utf-8')).encode('utf-8')) broadcast_data(sock,data) except: broadcast_data(sock, ("Client is offline").encode('utf-8')) print("Client is offline") sock.close() CONNECTION_LIST.remove(sock) continue
2)客户端
# telnet program example import socket, select, string, sys # def prompt(): # sys.stdout.write('<You> ') # sys.stdout.flush() # main function if __name__ == "__main__": if (len(sys.argv) < 3): print('Usage : python telnet.py hostname port') sys.exit() host = sys.argv[1] port = int(sys.argv[2]) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(2) # connect to remote host try: s.connect((host, port)) except: print('Unable to connect') sys.exit() print('Connected to remote host. Start sending messages') while 1: rlist = [sys.stdin, s] # Get the list sockets which are readable read_list, write_list, error_list = select.select(rlist, [], []) for sock in read_list: # incoming message from remote server if sock == s: data = sock.recv(4096) if not data: print(' Disconnected from chat server') sys.exit() else: # print data data = data.decode("utf-8") sys.stdout.write(data) print(len(data)) break
https://www.cnblogs.com/zhiyong-ITNote/p/7553694.html
https://blog.csdn.net/weixin_41010318/article/details/80257177