• epoll编写web服务器


    编写一个简单的web服务器,向每一个连接服务器的网页浏览器返回一行文本。

    脚本核心在web服务器的初始化过程中调用select.epoll(),注册服务器的文件描述符,已达到事件通知的目的。

     1 #!/usr/bin/env python
     2 #-*- coding:utf-8 -*-
     3 
     4 import socket
     5 import select
     6 import argparse
     7 
     8 SERVER_HOST = 'localhost'
     9 
    10 EOL1 = b'
    
    '
    11 EOL2 = b'
    
    '
    12 SERVER_RESPONSE = b"""HTTP/1.1 200 OK
    Date:Mon, 1 Apr 2013 01:01:01 GMT
    Content-Type:text/plain
    Content-Length: 25
    
    Hello from Epoll Server!"""
    13 
    14 class EpollServer(object):
    15     """ a socket server using epoll"""
    16     def __init__(self, host=SERVER_HOST, port=0):
    17         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    18         #创建套接字
    19         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    20         #设置当前套接字选项为可重用
    21         self.sock.bind((host, port))#绑定
    22         self.sock.listen(1)#监听
    23         self.sock.setblocking(0)#设置套接字模式为非阻塞
    24         self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    25         #socket阻塞模式自动开启Nagle算法。设置套接字选项关闭。Nagle算法用于对缓冲区内的一定数量的消息进行自动连接。
    26         print "Started Epoll Server"
    27         self.epoll = select.epoll()#创建epoll对象
    28         self.epoll.register(self.sock.fileno(), select.EPOLLIN)
    29            #为该socket的read event注册interest
    30            #EPOLLIN表示对应的文件描述符可以读,包括对端SOCKET正常关闭
    31            #fileno()返回的是该socket的一个整型文件描述符
    32 
    33     def run(self):
    34         """Executes epoll server operation"""
    35         try:
    36             connections = {}    #连接对象与socket对象的映射
    37             requests = {}        
    38             responses = {}
    39             while True:
    40                 events = self.epoll.poll(1)
    41                 #查询epoll对象是否可能发生任何interest的事件。1等待一秒
    42                 for fileno, event in events:#遍历事件
    43                     #如果事件发生在服务器
    44                     if fileno == self.sock.fileno():
    45                         connection, address = self.sock.accept()#接收客户端socket和地址
    46                         connection.setblocking(0)#设置非阻塞模式
    47                         self.epoll.register(connection.fileno(), select.EPOLLIN)
    48                         #为新的socket的read event注册兴趣                    
    49                         connections[connection.fileno()] = connection#添加到connections
    50                         requests[connection.fileno()] = b''
    51                         responses[connection.fileno()] = SERVER_RESPONSE#要发送的内容
    52 
    53                     #如果一个读事件发生在客户端,那么读取从客户端发来的新数据
    54                     elif event & select.EPOLLIN:
    55                         requests[fileno] += connections[fileno].recv(1024)
    56                         if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
    57                             self.epoll.modify(fileno, select.EPOLLOUT)
    58                              #注销对read event的interest,注册对write event的interest
    59                              print('-'*40 + '
    ' + requests[fileno].decode()[:-2])
    60                             #输出完整的请求,去除最后一个
    
    61 
    62                      #如果一个写事件发生在客户端,那么可能要接受来自客户端的新数据
    63                     elif event & select.EPOLLOUT:
    64                     #EPOLLOUT表示对应的文件描述符可以写
    65                         byteswritten = connections[fileno].send(responses[fileno])
    66                         responses[fileno] = responses[fileno][byteswritten:]
    67                         if len(responses[fileno]) == 0:#如果无响应
    68                             self.epoll.modify(fileno, 0)#禁用interest
    69                             connections[fileno].shutdown(socket.SHUT_RDWR)
    70                             #将对应的socket连接关闭
    71 
    72                     #如果一个中止事件发生在客户端
    73                     elif event & select.EPOLLHUP:
    74                     #EPOLLHUP表示对应的文件描述符被挂断
    75                         self.epoll.unregister(fileno)#注销客户端interest
    76                         connections[fileno].close()#关闭socket连接
    77                         del connections[fileno]#删除映射
    78         finally:
    79             self.epoll.unregister(self.sock.fileno())#注销服务器interest
    80             self.epoll.close()#关闭服务器epoll
    81             self.sock.close()#关闭服务器socket
    82 
    83 if __name__ == '__main__':
    84     parser = argparse.ArgumentParser(description='Socket Server Example with Epoll')
    85     parser.add_argument('--port', action="store", dest="port", type=int, required=True)
    86     given_args = parser.parse_args()
    87     port = given_args.port
    88     server = EpollServer(host=SERVER_HOST, port=port)
    89     server.run()

    参考文献: 《Python Network Programming Cookbook》

          http://scotdoyle.com/python-epoll-howto.html#source-code

  • 相关阅读:
    MySQL索引原理
    MyBatis执行流程的各阶段介绍
    实现单栈实现支持getMin的栈
    缓存LRU算法——使用HashMap和双向链表实现
    Hystrix资源隔离
    各种负载均衡策略-图解
    接口调用超时的实现原理
    Spring Bean生命周期的各阶段介绍
    TAR命令详解 复习
    INT MOD
  • 原文地址:https://www.cnblogs.com/tindin/p/4650799.html
Copyright © 2020-2023  润新知