上一篇文章我们提到了关于epoll模型的封装, 在这一节我们将在httpserver中使用我们封装好的selecploop和epollloop类
在httpserver主要有两个类,第一个是httprequest类
这个类主要是封装请求内容的合集,使用的这个类将request中的信息全部封装好,然后在代码中直接传递request类的实例就好了,比如说,要获取请求的header直接就是request.header()
就可以返回头部分的信息了.话不多说,直接贴代码,这次代码同样没有人任何的注释,我会用文字详细的解释每个方法的用途. 下面是httprequest类的代码:
class HttpRequest(object):
def __init__(self,header,body):
(self.request_method,
self.request_path,self.request_version)=header.splitlines()[0].rstrip('rn').split()
self.header = header
self.body =body
self.header_map= {}
try:
for lines in self.header.splitlines():
print lines
if lines.find(":")!=-1:
index = lines.index(':')
(key,value) = lines[:index],lines[index:]
self.header_map[key] = value
except:
print 'faile generator header map'
pass
def method(self):
return self.request_method
def path(self):
return self.request_path
def version(self):
return self.request_version
def body(self):
return self.body
def header(self):
return self.header
def get(self,name):
if self.header_map[name]:
return self.header_map[name]
我们在这个类里面把一个请求的内容全部分割好,当把这个类的实例传到其他的地方的时候,就可以直接是使用相应的方法获取我们想要的值.
下面来说说最关键的httpserver类
这个类似httpserver服务器的核心部分的代码,主要的功能是:
init()初始化socket套接字,同时设置一些相关的socket的配置,比如说SEND_BUF_SIZE ,RECV_BUF_SIZE ,socket.SO_REUSEADDR等socket属性
add_app()的主要功能是把我们下一节的application传入我们的httpserver类.我们后面具体在详细讲
- start_response(),finish_response(),这两个函数会被handle_one_request()调用,主要的功能就是把应用层返回到的结果组装成一个完整的http response ,然后使用sendall()函数返回给客户端
- run()是httpserver的启动函数,首先判断程序运行的平台选择对应的io复用模型,其次实例epoll或者select模型,最后调用selectloop.start()方法开始监听连接主循环.
下面呢就是精简版的Httpserver的代码:
class HttpServer(object):
"""strt httpserver"""
def __init__(self, (host,port),timeout =10,request_size=10):
self.clients = 0
self.clientmap={}
self.outputs = []
self.timeout = timeout
SEND_BUF_SIZE = 4096
RECV_BUF_SIZE = 1024
self.server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.server.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
self.server.bind((host,port))
self.server.listen(request_size)
def add_app(self,application):
self.application = application
def start_response(self,status,response_headers,exc_info = None):
self.header_set = (status,response_headers)
def finish_response(self,result):
try:
status,response_headers =self.header_set
print response_headers
response ='HTTP/1.1 {status}\r\n'.format(status = status)
if response_headers != []:
for header in response_headers:
response += '{0}: {1}\r\n'.format(*header)
else:response+='\r\n'
response +='\r\n'
response += result
return response
except:
print '*****error in finish_response****** '
return 'error in finish_response'
def parse_request(self,text):
try:
(header,body) = text.split('\r\n\r\n')
except:
header =text
body =''
return HttpRequest(header,body)
def handle_one_request(self,data):
print (''.join(
'{line}'.format(line= line)
for line in data.splitlines()
))
result = self.application(self.parse_request(data))
print 'there is result ....'
self.start_response(result[0],result[1])
return self.finish_response(result[2])
def run(self):
print '>>>>>>>>>>>>>start run server <<<<<<<<<<<<<<<'
try:
sysstr = platform.system()
except:
sysstr = "Other System"
if sysstr== "Windows":
print 'using SelectLoop on Windows'
loop = SelectLoop(self.server,self.timeout,self.handle_one_request)
loop.start()
elif sysstr=="Linux":
print 'using EPollLoop on Linux'
loop = EPollLoop(self.server,self.timeout,self.handle_one_request)
loop.start()
进入主循环之后,epoll或者select就不断的监听新进入的连接,它们都维持一个文件描述符的队列,所有的socket连接的文件描述符都在里面.
当一个新的连接来临时,epoll通过epoll_ctl()来注册一个文件描述符 到监听的队列,同时分配一个回调函数(主要的功能是等该连接准备就绪时把该连接加入到就绪表里面),当有一个或者多个连接准备就绪时,就开始进入业务处理遍历 ,我们将就绪表里面的对象全部遍历一遍,接受里面的数据或者是发送数据.同时把数据通过回调函数来处理,
可以看到我们这里的回调函数就是 handle_one_request() ,好了收到的数据已经到了 我们的回调函数handle_one_request() 里面了,让我们看看handle_one_request() 函数主要是做什么的: 首先handle_one_request() 将接收到的数据通过parse_request()函数来处理,返回一个httprequest类的实例,然后把这个实例传入到application里面
self.application(self.parse_request(data))这是有一个很关键的代码
到这里我们的httpserver算是完成了,接下来要把舞台交给我们应用层了,这个application有点WSGI的感觉(Web Server Gateway Interface)
下一篇将进入应用层来讨论构建应用框架的内容
All rights reserved.