epoll 的使用

#

epoll的應用不少,nginx,tornado,乃至攜程,都跟它又關係.因此趁着失業的無聊的事件,從基礎學起,瞭解下epoll的用法html

epoll 在python的api

Python包含了訪問Linux epoll庫的API。這篇文章用幾個簡單的例子來展現下這個APIpython

經常使用api

select.epoll() #返回建立epoll對象
epoll.register(fd[, eventmask]) #將fd的事件註冊到epoll對象中
epoll.unregister(fd) #去除fd
epoll.poll([timeout=-1[, maxevents=-1]]) #等待事件
epoll.modify(fd, eventmask) #更改fd關注的事件

更多api文檔,能夠在dash中查看nginx

經常使用事件常量

EPOLLIN     可讀事件
EPOLLOUT    可寫事件
EPOLLERR    錯誤事件
EPOLLHUP    掛起事件

示例代碼

如下是簡單的helloword 程序,運行程序後,瀏覽器訪問localhost:8080/ 輸出hellowordapi

# coding: utf-8
import socket, select
from ipdb import set_trace
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(10240)
serversocket.setblocking(0)
serversocket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

#建立epoll對象,epoll對象是個存儲"fd-事件"的容器,把關注的"fd-事件"註冊到容器中,
#接下來就能夠監聽到fd的事件
epoll = select.epoll()

#將server的sockect註冊到epoll中,由於此示例程序功能是瀏覽器顯示helloword,
#因此關注接入的客戶端fd,當有客戶端鏈接時,出發的必定是server sockect的EPOLLIN
epoll.register(serversocket.fileno(), select.EPOLLIN)

try:
   connections = {}; requests = {}; responses = {}
   while True:
      events = epoll.poll(10)
      # print '=='*10
      for fileno, event in events:
         #客戶端接入時,註冊客戶端fd到epoll,第一步須要讀取客戶端發送到服務端的信息,因此用EPOLLIN
         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:
            data=connections[fileno].recv(1024)

            requests[fileno] += data
            #判斷客戶端信息是否讀取完畢
            if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
               epoll.modify(fileno, select.EPOLLOUT)
               print('-'*40 + str(fileno) + '\n' + requests[fileno].decode()[:-2])
            #處理客戶端關閉請求時的信息,防止服務端程序出現close_wait
            #使用telnet測試後發現,客戶端主動關閉時會發送個空信息到客戶端,不處理的話,會出現close_wait,
            #而且循環中每次都會出現該事件,會嚴重影響程序處理效率,所以須要把它從epoll重移除
            if data == b'':
               print "receiv client close : %s "% str(fileno)
               epoll.unregister(fileno)
               try :
                  connections[fileno].close()
               except Exception, e:
                  print ' connection was allready closed.....'+e
         #將此接入返回給客戶端
         elif event & select.EPOLLOUT:
            byteswritten = connections[fileno].send(responses[fileno])
            responses[fileno] = responses[fileno][byteswritten:]
            if len(responses[fileno]) == 0:
               epoll.modify(fileno, 0)
               try:
                  connections[fileno].shutdown(socket.SHUT_RDWR)
               except Exception, e:
                  print ' connection was allready closed.....' + e
         #服務端主動關閉鏈接時的邏輯處理
         elif event & select.EPOLLHUP:
            print "close fd : %s "% str(fileno)
            epoll.unregister(fileno)
            connections[fileno].close()
            del connections[fileno]
         else :
            print '='*10
            print fileno,event
finally:
   # set_trace()
   epoll.unregister(serversocket.fileno())
   epoll.close()
   serversocket.close()

總結

這段程序大部分摘抄自http://scotdoyle.com/python-epoll-howto.html
但通過測試發現他的程序有些bug.
使用telnet測試後發現,客戶端主動關閉時會發送個空信息到客戶端,不處理的話,會出現close_wait.
而且循環中每次都會出現該事件,會嚴重影響程序處理效率,所以須要把它從epoll重移除.
緣由發生tcp協議中四次握手時,客戶端的關閉消息沒有被處理瀏覽器

理解這些bug對epoll使用頗有幫助socket

相關文章
相關標籤/搜索