多路复用有select,poll为啥还要epoll呢?文章http://blog.csdn.net/songfreeman/article/details/51179213给出了详细讲解,这里不再赘述。我通过python pdb调试了《Python网络编程攻略》第34页的select.epoll多路复用web服务器程序后,有了较为粗浅的认识,哈哈!与大家分享!
select.epoll大概是一个能够监测在其上面注册过所有socket对象的东西,当socket的状态发生改变时能够及时的对其进行处理,从而实现多路复用。
服务器代码
'''
Created on 2017-3-8
@author: lenovo
'''
import socket
import select
import argparse
import pdb
SERVER_HOST=
'localhost'
EOL1 =
b'\n\n'
EOL2 =
b'\n\r\n'
SERVER_RESPONSE =
b"""HTTP/1.1 200 OK\r\n Data: Mon, 1 Apr 2013 01:01:01\
GMT\r\nContent-Type: text/plain\r\nContent-Length: 25\r\n\r\n\
Hello from Epoll server!"""
class EpollServer(object):
""" A socket server using Epoll. """
def __init__(self,host=SERVER_HOST,port=0):
self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
self.sock.bind((host,port))
self.sock.listen(
1)
self.sock.setblocking(
0)
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY,
1)
print "Start Epoll Server"
self.epoll = select.epoll()
self.epoll.register(self.sock.fileno(),select.EPOLLIN)
def run(self):
"""Executes epoll server operation"""
try:
connections = {}; requests = {}; responses = {}
while True:
events = self.epoll.poll(
1)
for fileno,event
in events:
if fileno == self.sock.fileno():
connection, address = self.sock.accept()
connection.setblocking(
0)
self.epoll.register(connection.fileno(),select.EPOLLIN)
connections[connection.fileno()] = connection
requests[connection.fileno()]=
b''
responses[connection.fileno()]=SERVER_RESPONSE
elif event & select.EPOLLIN:
requests[fileno]+=connections[fileno].recv(
1024)
if EOL1
in requests[fileno]
or EOL2
in requests[fileno]:
self.epoll.modify(fileno,select.EPOLLOUT)
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:
self.epoll.modify(fileno,
0)
connections[fileno].shutdown(socket.SHUT_RDWR)
elif event & select.EPOLLHUP:
self.epoll.unregister(fileno)
connections[fileno].close()
del connections[fileno]
finally:
self.epoll.unregister(self.sock.fileno())
self.epoll.close()
self.sock.close()
if __name__ ==
'__main__':
parser = argparse.ArgumentParser(description=
'Socket Server Example with Epoll')
parser.add_argument(
'--port',action=
'store',dest=
"port",type=int,required=
True)
pdb.set_trace()
given_args = parser.parse_args()
port = given_args.port
server = EpollServer(host=SERVER_HOST,port=port)
server.run()
调试过程
发现python中的pdb还挺好用的!开始吧!
在server = EpollServer(host=SERVER_HOST,port=port)设置断点,进入关键部分。
创建服务器socket–self.sock,如下图。
创建服务器socket后,再创建select.epoll,然后将socket在其epoll上注册:
self
.epoll = select
.epoll() self
.epoll.register(self
.sock.fileno(),select
.EPOLLIN)
然后,继续,这时我们的epoll已经开始监控服务器socket了,不信继续往下看。如下图,运行了events = self.epoll.poll(1)后,epoll就开始监控了,奇怪为什么返回的events是空呢[]?
这是因为没有相应的网页请求,好就加一个吧,注意输入localhost:8800
回来继续调试:
哦耶,来了,我们看到events=[(3,1)],其中3是服务器socket的句柄fd,1是select.EPOLLIN的值,这说明服务器接收到了服务请求。来看看它怎么处理呢?
我们看到程序进入了if fileno == self.sock.fileno():这个if条件是要判断监测到的句柄fd是不是服务器socket,如果是则按照相应的程序进行处理,处理的方法是创建新的socket对象connection处理请求,并将其在epoll中注册,设置为EPOLLIN,即接收信息,对其进行监控处理。以下是新建的connection的信息,它的句柄fd=5:
对新建的connection的请求内容requests和返回内容进行指定:
继续运行,再运行一次events = self.epoll.poll(1)后,events的值变成了[(5,1)],这是因为epoll监测到了我们之间注册的connection。
ok,继续运行,由于event=1,故进入接受信息的处理过程。
接收信息的过程是:首先,将收到的信息存入requests中,然后查看该信息是否来自浏览器,方法是执行if EOL1 in requests[fileno] or EOL2 in requests[fileno]:,如果是浏览器发来的请求信息,则准备反馈消息给浏览器,方法是它在epoll中的标志位改为select.EPOLLOUT。
经过再一次循环执行events = self.epoll.poll(1),我们发现events的值变成了[(5,4)],这是由于在上面将connection在epoll中的标志位改为select.EPOLLOUT的缘故。
继续调试,程序会进入发消息阶段,发送消息后将对应的response重置为空。然后将connection的EPOLL标志位置为0。
将会在浏览器中看到:
此时,一定要果断关掉浏览器,否则它将再一次向epoll服务器发请求,你将再次看到重复上面的内容。再一次运行events = self.epoll.poll(1)将得到events的值变成了[(5,16)]:
程序将运行到以下位置,将connection从epoll中解除注册,并关闭。
我们将看到events重新变成了空:
ok,good night!
转载请注明原文地址: https://ju.6miu.com/read-25630.html