1. 程式人生 > >python之epoll服務器源碼分析

python之epoll服務器源碼分析

ip地址 req style length 套接字 更新 發送 長度 nts

#!/usr/bin/env python
# -*- coding: utf8 -*-

import socket, select

EOL1 = b/r/n
EOL2 = b/r/n/r/n

# 拼接成的response
response = bHTTP/1.0 200 OK/r/nDate: Mon, 1 Jan 1996 01:01:01 GMT/r/n
response += bContent-Type: text/plain/r/nContent-Length: 13/r/n/r/n
response += bHello, world!

# 創建一個服務端的socket,來監聽是否有請求過來
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) # 設置時非阻塞 print(serversocket.fileno()) epoll = select.epoll() # 準備設計一個IO多路復用 epoll.register(serversocket.fileno(),
select.EPOLLIN) # 把上面的serversocket的fileno() 和 監聽準備讀信號 註冊到epoll中去 try: # 設置三個空字典 connections = {} requests = {} responses = {} while True: # 查看epoll是否有信號有的話,就放在events中 events = epoll.poll(1) # 循環events,分別拿到 文件描述號 和對應的事件 for fileno, event
in events: # 如果當前的文件描述號是serversocket,那麽說明有新的連接 if fileno == serversocket.fileno(): # 所以就得接受,創建了 連接,拿到了對方的IP地址 connection, address = serversocket.accept() # connection就是客戶端連接過來建立的socket,設置為非阻塞 connection.setblocking(0) # 客戶端建立的socket也註冊到select模塊的IO多路復用中去 epoll.register(connection.fileno(), select.EPOLLIN) # 以Connection的文件描述號 作為鍵 socket作為值保存在connections中 connections[connection.fileno()] = connection # 同時在requests和responses字典中, # requests中 以connection.fileno() 作為鍵 以請求的內容作為值 # responses中 以connection.fileno() 作為鍵 以相應的內容作為值,這個我們返回的是固定的,僅僅返回hello world requests[connection.fileno()] = b‘‘ responses[connection.fileno()] = response # 如果請求的數據不是socketserver,那肯定是客戶端的,判斷是否是準備讀的信號 elif event & select.EPOLLIN: # 立馬來開始讀取數據,加到requests對象套接字的內容中去 requests[fileno] += connections[fileno].recv(1024) # 判斷換行 和 兩個換行是否在接收過來的數據中 if EOL1 in requests[fileno] or EOL2 in requests[fileno]: # 如果是的話,就將這個套接字的監聽更新為準備寫 epoll.modify(fileno, select.EPOLLOUT) # 打印40個-,然後換行,加上請求的內容 print(- * 40 + /n + requests[fileno].decode()[:-2]) # 如果請求的數據不是socketserver,那肯定是客戶端的,判斷是否是準備寫的信號 elif event & select.EPOLLOUT: # 立馬來開始讀取數據,就將response的對應的套接字號對應的值拿出來,其實就是hello world,O(∩_∩)O哈哈~ # 並統計發送了多少個字節 byteswritten = connections[fileno].send(responses[fileno]) # 更新response對應套接字內容為剩下的內容 responses[fileno] = responses[fileno][byteswritten:] # 如果內容發完了,剩下的長度就是0,如果長度是0 if len(responses[fileno]) == 0: # 就修改select,不監聽該套接字的內容,其就變成了EPOLLHUP epoll.modify(fileno, 0) # 然後關閉該socket的管道 connections[fileno].shutdown(socket.SHUT_RDWR) elif event & select.EPOLLHUP: # 如果不監聽該套接字的內容,就將其註銷掉 epoll.unregister(fileno) # 關閉該套接字 connections[fileno].close() # 從連接中刪除該文件描述符 del connections[fileno] finally: # 最後關閉serversocket服務器套接字 epoll.unregister(serversocket.fileno()) # 關閉epoll epoll.close() # 套接字關閉 serversocket.close()

python之epoll服務器源碼分析