python实现select和epoll模型socket网络编程
2015-12-08 13:11
891 查看
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实
上从现在看来,这也是它所剩不多的优点之一,现在其实更多的人用epoll,在
python下epoll文档有点少,就先讲究搞搞select ~
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在
Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一
限制。
说点我的理解,要是用烦了多线程的网络编程,可以试试select的模型。
传递给
客户端的代码:
#coding:utf-8
import socket,select
#xiaorui.cc
host = "localhost"
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
s.send("coming from select client")
s.close()
一个完成的select的例子:
import select
import socket
import Queue
import time
import os
#创建socket 套接字
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
#配置参数
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , 1)
server_address= ('192.168.0.101',9999)
server.bind(server_address)
server.listen(10)
inputs = [server]
outputs = []
message_queues = {}
#timeout = 20
while inputs:
print "waiting for next event"
# readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout) 最后一个是超时,当前连接要是超过这个时间的话,就会kill
readable , writable , exceptional = select.select(inputs, outputs, inputs)
# When timeout reached , select return three empty lists
if not (readable or writable or exceptional) :
print "Time out ! "
break;
for s in readable :
if s is server:
#通过inputs查看是否有客户端来
connection, client_address = s.accept()
print " connection from ", client_address
connection.setblocking(0)
inputs.append(connection)
message_queues[connection] = Queue.Queue()
else:
data = s.recv(1024)
if data :
print " received " , data , "from ",s.getpeername()
message_queues[s].put(data)
# Add output channel for response
if s not in outputs:
outputs.append(s)
else:
#Interpret empty result as closed connection
print " closing", client_address
if s in outputs :
outputs.remove(s)
inputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
for s in writable:
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
print " " , s.getpeername() , 'queue empty'
outputs.remove(s)
else:
print " sending " , next_msg , " to ", s.getpeername()
os.popen('sleep 5').read()
s.send(next_msg)
for s in exceptional:
print " exception condition on ", s.getpeername()
#stop listening for input on the connection
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
上从现在看来,这也是它所剩不多的优点之一,现在其实更多的人用epoll,在
python下epoll文档有点少,就先讲究搞搞select ~
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在
Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一
限制。
说点我的理解,要是用烦了多线程的网络编程,可以试试select的模型。
传递给
select的参数是几个列表,分别表示读事件、写事件和错误事件。
select方法返回三个列表,其中包含满足条件的对象(读、写和异常)。
服务端的代码: #coding:utf-8 import socket,select import time import os #xiaorui.cc host = "localhost" port = 50000 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind((host,port)) s.listen(5) while 1: infds,outfds,errfds = select.select([s,],[],[],5) if len(infds) != 0: clientsock,clientaddr = s.accept() buf = clientsock.recv(8196) if len(buf) != 0: print (buf) os.popen('sleep 10').read() clientsock.close() # print "no data coming"
客户端的代码:
#coding:utf-8
import socket,select
#xiaorui.cc
host = "localhost"
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
s.send("coming from select client")
s.close()
一个完成的select的例子:
import select
import socket
import Queue
import time
import os
#创建socket 套接字
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
#配置参数
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , 1)
server_address= ('192.168.0.101',9999)
server.bind(server_address)
server.listen(10)
inputs = [server]
outputs = []
message_queues = {}
#timeout = 20
while inputs:
print "waiting for next event"
# readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout) 最后一个是超时,当前连接要是超过这个时间的话,就会kill
readable , writable , exceptional = select.select(inputs, outputs, inputs)
# When timeout reached , select return three empty lists
if not (readable or writable or exceptional) :
print "Time out ! "
break;
for s in readable :
if s is server:
#通过inputs查看是否有客户端来
connection, client_address = s.accept()
print " connection from ", client_address
connection.setblocking(0)
inputs.append(connection)
message_queues[connection] = Queue.Queue()
else:
data = s.recv(1024)
if data :
print " received " , data , "from ",s.getpeername()
message_queues[s].put(data)
# Add output channel for response
if s not in outputs:
outputs.append(s)
else:
#Interpret empty result as closed connection
print " closing", client_address
if s in outputs :
outputs.remove(s)
inputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
for s in writable:
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
print " " , s.getpeername() , 'queue empty'
outputs.remove(s)
else:
print " sending " , next_msg , " to ", s.getpeername()
os.popen('sleep 5').read()
s.send(next_msg)
for s in exceptional:
print " exception condition on ", s.getpeername()
#stop listening for input on the connection
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
关于epoll的方面,大家可以看看这个老外的文档,写不错 ~ select是轮询、epoll是触发式的,所以epoll的效率高。 参考的文档地址:http://scotdoyle.com/python-epoll-howto.html 下面是用epoll实现一个服务端 ~ import socket, select EOL1 = b'\n\n' EOL2 = b'\n\r\n' response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n' response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n' response += b'Hello, world!' serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) serversocket.bind(('0.0.0.0', 8080)) serversocket.listen(1) serversocket.setblocking(0) epoll = select.epoll() epoll.register(serversocket.fileno(), select.EPOLLIN) try: connections = {}; requests = {}; responses = {} while True: events = epoll.poll(1) for fileno, event in events: if fileno == serversocket.fileno(): connection, address = serversocket.accept() connection.setblocking(0) epoll.register(connection.fileno(), select.EPOLLIN) connections[connection.fileno()] = connection requests[connection.fileno()] = b'' responses[connection.fileno()] = response elif event & select.EPOLLIN: requests[fileno] += connections[fileno].recv(1024) if EOL1 in requests[fileno] or EOL2 in requests[fileno]: epoll.modify(fileno, select.EPOLLOUT) connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1) print('-'*40 + '\n' + requests[fileno].decode()[:-2]) elif event & select.EPOLLOUT: byteswritten = connections[fileno].send(responses[fileno]) responses[fileno] = responses[fileno][byteswritten:] if len(responses[fileno]) == 0: connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 0) epoll.modify(fileno, 0) connections[fileno].shutdown(socket.SHUT_RDWR) elif event & select.EPOLLHUP: epoll.unregister(fileno) connections[fileno].close() del connections[fileno] finally: epoll.unregister(serversocket.fileno()) Epoll的最大好处是不会随着FD的数目增长而降低效率,在select中采用轮询处理,每个fd的处理情况,而epoll是维护一个队列,直接看队列是不是空就可以了。
相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例
- Python 七步捉虫法