python 的select服務(wù)器模型
python提供了操作系統(tǒng)底層select接口的封裝版本,使用起來(lái)會(huì)更加方便速勇。
select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
rlist wlist xlist都是列表數(shù)據(jù)結(jié)構(gòu)。返回的列表則是對(duì)應(yīng)的可讀表疙筹,可寫表福青,有異常表。
import select
from socket import *
def main():
# 1. 創(chuàng)建socket套接字
tcp_server = socket(AF_INET, SOCK_STREAM)
# 2. 綁定IP地址和端口
tcp_server.bind(("", 6699))
# 3. 把主動(dòng)模式變成被動(dòng)模式 只能接收 不能發(fā)送數(shù)據(jù)
tcp_server.listen(5)
# 4. 聲明一個(gè)列表
inputs_list = [tcp_server]
while True:
# 阻塞在這里,內(nèi)核監(jiān)聽(tīng)inputs_list里的套接字,如果狀態(tài)改變就進(jìn)行解阻塞, 并返回有變化的套接字
read_list, _, _ = select.select(inputs_list, [], [])
# 循環(huán)read_list
for sock in read_list:
# 判斷是不是被動(dòng)的套接字
if sock == tcp_server:
# 創(chuàng)建新的套接字
new_socket, client_info = sock.accept()
# 將創(chuàng)建好的套接字添加到列表中
inputs_list.append(new_socket)
print(f"連接到{client_info}")
else:
# 接收數(shù)據(jù)
raw_data = sock.recv(1024)
if raw_data:
# 發(fā)送數(shù)據(jù)
sock.send(raw_data)
print(f"收到{client_info}的數(shù)據(jù){raw_data.decode('gb2312')}")
else:
# 斷開(kāi)連接
sock.close()
# 將套接字從列表中移除
inputs_list.remove(sock)
if __name__ == '__main__':
main()
-
32系統(tǒng)只能1024個(gè)連接 64位 2048個(gè)
2.采用輪詢機(jī)制绕辖,當(dāng)并發(fā)大的時(shí)候稽鞭,就會(huì)崩了
? POLL只是突破了連接的限制
python 的epoll服務(wù)器模型
為了解決select和poll輪詢機(jī)制的效率問(wèn)題,從Linux2.6內(nèi)核開(kāi)始引镊,引入了epoll接口朦蕴。
沒(méi)有最大并發(fā)連接的限制,能打開(kāi)的文件描述符的上限遠(yuǎn)大于1024弟头。
效率提升吩抓,不是輪詢的方式,而是采用了事件通知機(jī)制赴恨,當(dāng)文件描述符配置為epoll方式后疹娶,一旦事件發(fā)生,設(shè)備驅(qū)動(dòng)會(huì)主動(dòng)通知內(nèi)核伦连,此時(shí)應(yīng)用空間會(huì)得到通知雨饺。
應(yīng)用空間的處理機(jī)制幾乎不變,主要是內(nèi)核結(jié)構(gòu)的修改惑淳。
from socket import *
import select
def main():
# 創(chuàng)建socket套接字
tcp_server = socket(AF_INET, SOCK_STREAM)
# 重復(fù)使用綁定的信息
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 綁定端口,ip
tcp_server.bind(("", 6699))
# 把主動(dòng)模式變?yōu)楸粍?dòng)模式,只能接收,不能發(fā)送數(shù)據(jù)
tcp_server.listen(5)
# 創(chuàng)建epoll()對(duì)象
epoll = select.epoll()
# 將被動(dòng)套接字添加到epoll對(duì)象中
print(f"被動(dòng)套接字:{tcp_server.fileno()}")
epoll.register(tcp_server.fileno(), select.EPOLLIN)
# 聲明一個(gè)字典,用來(lái)存儲(chǔ)新的套接字
new_sockets = {}
# 聲明一個(gè)字典,用來(lái)存儲(chǔ)客戶端信息
client_infos = {}
while True:
print(f"所有新的套接字{new_sockets}")
print(f"所有客戶端信息{client_infos}")
# 阻塞在這里,等待套接字狀態(tài)改變
epoll_list = epoll.poll()
print(f"epoll_list:{epoll_list}")
# 循環(huán)epoll_list
for fd, events in epoll_list:
print(f"fd:{fd},events:{events}")
# 判斷fd是不是被動(dòng)套接字
if fd == tcp_server.fileno():
# 創(chuàng)建新的套接字
new_socket, client_info = tcp_server.accept()
print(f"連接到{new_socket}")
# 將新的套接字接入到epoll監(jiān)聽(tīng)列表
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]}的數(shù)據(jù){raw_data.decode('gb2312')}")
else:
# 關(guān)閉套接字
new_sockets[fd].close()
# 注銷監(jiān)聽(tīng)隊(duì)列
epoll.unregister(fd)
# 字典刪除
del new_sockets[fd]
del client_infos[fd]
if __name__ == '__main__':
main()