python 的select服务器模型
python提供了操作系统底层select接口的封装版本,使用起来会更加方便。
select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
rlist wlist xlist都是列表数据结构。返回的列表则是对应的可读表,可写表,有异常表。
import select
from socket import *
def main():
# 1. 创建socket套接字
tcp_server = socket(AF_INET, SOCK_STREAM)
# 2. 绑定IP地址和端口
tcp_server.bind(("", 6699))
# 3. 把主动模式变成被动模式 只能接收 不能发送数据
tcp_server.listen(5)
# 4. 声明一个列表
inputs_list = [tcp_server]
while True:
# 阻塞在这里,内核监听inputs_list里的套接字,如果状态改变就进行解阻塞, 并返回有变化的套接字
read_list, _, _ = select.select(inputs_list, [], [])
# 循环read_list
for sock in read_list:
# 判断是不是被动的套接字
if sock == tcp_server:
# 创建新的套接字
new_socket, client_info = sock.accept()
# 将创建好的套接字添加到列表中
inputs_list.append(new_socket)
print(f"连接到{client_info}")
else:
# 接收数据
raw_data = sock.recv(1024)
if raw_data:
# 发送数据
sock.send(raw_data)
print(f"收到{client_info}的数据{raw_data.decode('gb2312')}")
else:
# 断开连接
sock.close()
# 将套接字从列表中移除
inputs_list.remove(sock)
if __name__ == '__main__':
main()
-
32系统只能1024个连接 64位 2048个
2.采用轮询机制,当并发大的时候,就会崩了
POLL只是突破了连接的限制
python 的epoll服务器模型
为了解决select和poll轮询机制的效率问题,从Linux2.6内核开始,引入了epoll接口。
没有最大并发连接的限制,能打开的文件描述符的上限远大于1024。
效率提升,不是轮询的方式,而是采用了事件通知机制,当文件描述符配置为epoll方式后,一旦事件发生,设备驱动会主动通知内核,此时应用空间会得到通知。
应用空间的处理机制几乎不变,主要是内核结构的修改。
from socket import *
import select
def main():
# 创建socket套接字
tcp_server = socket(AF_INET, SOCK_STREAM)
# 重复使用绑定的信息
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 绑定端口,ip
tcp_server.bind(("", 6699))
# 把主动模式变为被动模式,只能接收,不能发送数据
tcp_server.listen(5)
# 创建epoll()对象
epoll = select.epoll()
# 将被动套接字添加到epoll对象中
print(f"被动套接字:{tcp_server.fileno()}")
epoll.register(tcp_server.fileno(), select.EPOLLIN)
# 声明一个字典,用来存储新的套接字
new_sockets = {}
# 声明一个字典,用来存储客户端信息
client_infos = {}
while True:
print(f"所有新的套接字{new_sockets}")
print(f"所有客户端信息{client_infos}")
# 阻塞在这里,等待套接字状态改变
epoll_list = epoll.poll()
print(f"epoll_list:{epoll_list}")
# 循环epoll_list
for fd, events in epoll_list:
print(f"fd:{fd},events:{events}")
# 判断fd是不是被动套接字
if fd == tcp_server.fileno():
# 创建新的套接字
new_socket, client_info = tcp_server.accept()
print(f"连接到{new_socket}")
# 将新的套接字接入到epoll监听列表
epoll.register(new_socket.fileno(), select.EPOLLIN)
# 将新的套接字存入字典
new_sockets[new_socket.fileno()] = new_socket
# 将新的客户端存入字典
client_infos[new_socket.fileno()] = client_info
else:
# 接收信息
raw_data = new_sockets[fd].recv(1024)
# 判断
if raw_data:
print(f"收到{client_infos[fd]}的数据{raw_data.decode('gb2312')}")
else:
# 关闭套接字
new_sockets[fd].close()
# 注销监听队列
epoll.unregister(fd)
# 字典删除
del new_sockets[fd]
del client_infos[fd]
if __name__ == '__main__':
main()
网友评论