使用Python模块中的select模块实现web聊天室功能
select模块
Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqueue方法(freeBSD系统)
参数: 可接受四个参数(前三个必须)
rlist: wait until ready for reading
wlist: wait until ready for writing
xlist: wait for an “exceptional condition”
timeout: 超时时间
select方法:
每次调用slect都要将所有的fd拷贝到内核空间(每次都要拷贝),导致效率下降
每次调用slect都要将所有的fd拷贝到内核空间(每次都要拷贝),导致效率下降
监听的的实现是通过遍历所有的fd,(遍历消耗的时间消耗多)判断是否有数据访问
最大连接数(input中放的文件描述符数量1024)
pull方法:
最大连接数没有限制了,除此之外和select一样。使用较少
epull方法:
内部通过3个函数实现(select是其中一个)
第一个函数:
创建epoll句柄,把所有的fd拷贝到内核空间,只需要拷贝一次
第二个函数: 回调函数
某一个函数或者动作成功完成后,会自动触发一个函数为所有的fd绑定一个回调函数,一旦有数据访问,触发改回调函数,回调函数把fd放到链表中。(只要有活动,把fd放到链表中,动态监听)这样就提高了效率。例子:交试卷
第三个函数,判断链表是否为空
server端代码
#/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
import select
# 封装
class SelectServer(object):
# 定义主函数
def __init__(self, host, port, backlog):
self.host = host
self.port = port
self.address = (host, port)
self.backlog = backlog
self.server = None
self.socketList = list()
def _initSocket(self):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.address)
self.server.listen(self.backlog)
self.socketList.append(self.server)
print("chat room has start!")
while 1:
rlist, wlist, elist = select.select(self.socketList, [], [])
for r in rlist:
if r == self.server:
serverConn, clienAddr = self.server.accept()
self.socketList.append(serverConn)
print("{0}进入了房间".format(clienAddr))
self.broadcast(r, "{0}进入了房间".format(clienAddr))
else:
try:
data = r.recv(2048)
if data:
print("{0}: {1}".format(clienAddr, data))
self.broadcast(r, "{0}: {1}".format(clienAddr, data))
except Exception as e:
self.broadcast(r, "{0}下线".format(clienAddr))
print("{0}下线".format(clienAddr))
r.close()
self.socketList.remove(r)
self.server.close()
# 定义广播函数
def broadcast(self, r, data):
for i in self.socketList:
if i != r and i != self.server:
try:
i.sendall(data)
except:
i.close()
self.socketList.remove(i)
# 定义main函数
def main():
selectServer = SelectServer(host="192.168.154.131", port=9999, backlog=5)
selectServer._initSocket()
if __name__ == '__main__':
main()
client端代码
#/usr/bin/env python
#-*- coding:utf-8 -*-
import socket, select, string, sys
import time
# main function
if __name__ == "__main__":
host = "192.168.154.131"
port = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
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]
read_list, write_list, error_list = select.select(rlist, [], [])
for sock in read_list:
if sock == s:
data = sock.recv(2048)
if not data:
continue
else:
sys.stdout.write(data)
else:
msg = raw_input("我说: ")
s.sendall(msg)