大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这期内容当中小编将会给大家带来有关Python中select和selectors的用法,以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
网站建设、网站制作服务团队是一支充满着热情的团队,执着、敏锐、追求更好,是创新互联的标准与要求,同时竭诚为客户提供服务是我们的理念。成都创新互联公司把每个网站当做一个产品来开发,精雕细琢,追求一名工匠心中的细致,我们更用心!1. select模块
针对select,要先理解其他几个概念:
文件描述符:
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
内核空间:
Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,Linux的虚拟地址空间也为0~4G。Linux内核将这4G字节的空间分为两部分。将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址 0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间)。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。
内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。
内核空间和用户空间一般通过系统调用进行通信。
select就是针对许多文件描述符(简称fd)进行监控,它有三个参数:
第一个参数监控 进来的 数据的fd列表,select监控这个列表,等待这些fd发送过来数据,一旦数据发送过来了(可以读取了),就返回一个可读的fd列表
第二个参数监控 出去的 数据的fd列表,select监控这个列表,等待这些fd发送出去数据,一旦fd准备好发送了(可以写入了),就返回一个可写的fd列表
第三个参数监控fd列表,返回出异常的fd列表
服务端:
import select import socket import sys import queue # 生成socket对象 server = socket.socket() # 设置非阻塞模式 server.setblocking(False) # 绑定地址,设置监听 server.bind(('localhost',9999)) server.listen(5) # 将自己也放进待监测列表里 inputs = [server, ] outputs = [] message_queues = {} while True: ''' 关于socket可读可写的判断,可以参考博客:https://blog.csdn.net/majianfei1023/article/details/45788591 ''' rlist, wlist, elist = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里 for r in rlist: # 遍历已经可以准备读取数据的 fd if r is server: # 如果这个 fd 是server,即 server 有数据待接收读取,说明有新的客户端连接过来了 conn, client_addr = r.accept() print("new connection from",client_addr) conn.setblocking(False) inputs.append(conn) # 将这个新的客户端连接添加到检测的列表中 message_queues[conn] = queue.Queue() # 用队列存储客户端发送来的数据,等待服务器统一返回数据 else: # 这个可读的 r 不是服务器,那就是某个客户端。就是说客户端发送数据过来了,这些数据处于待读取状态 try: # 异常处理,这是为了防止客户端异常断开报错(比如手动关掉客户端黑窗口,服务器也会跟着报错退出) data = r.recv(1024) if data: # 根据判断data是否为空,判断客户端是否断开 print("收到来自[%s]的数据:" % r.getpeername()[0], data) message_queues[r].put(data) # 收到的数据先放到queue里,一会返回给客户端 if r not in outputs: outputs.append(r) # 放进可写的fd列表中,表明这些 fd 已经准备好去发送数据了。 else: # 如果数据为空,表明客户端断开了 print('客户端断开了') if r in outputs: outputs.remove(r) # 清理已断开的连接 inputs.remove(r) # 清理已断开的连接 del message_queues[r] # 清理已断开的连接 except ConnectionResetError: # 如果报错,说明客户端断开了 print("客户端异常断开了", r) if r in outputs: outputs.remove(r) # 清理已断开的连接 inputs.remove(r) # 清理已断开的连接 del message_queues[r] # 清理已断开的连接 for w in wlist: # 遍历可写的 fd 列表,即准备好发送数据的那些fd # 判断队列是否为空 try : next_msg = message_queues[w].get_nowait() except queue.Empty: # print("client [%s]" % w.getpeername()[0], "queue is empty..") outputs.remove(w) # 队列不为空,就把队列中的数据改成大写,原样发回去 else: # print("sending msg to [%s]"% w.getpeername()[0], next_msg) w.send(next_msg.upper()) for e in elist: # 处理报错的 fd e.close() print("Error occured in ",e.getpeername()) inputs.remove(e) if e in outputs: outputs.remove(e) del message_queues[e]