基于epoll实现的I/O复用HTTP服务器
source link: https://allenwind.github.io/blog/2622/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
NLP、深度学习、机器学习、Python、Go
基于epoll实现的I/O复用HTTP服务器
I/O复用HTTP服务器
在这里总结几个实用的、起启发作用的网络编程实例。这些实例可能并不局限特定编程语言且不按难度排序。该文章会持续更新。目前包括的实例:
- 快速文件服务
- 使用epoll实现的I/O复用简易HTTP服务器
快速创建文件服务
快速创建一个文件服务,方便设备间共享文件。
- Golang版本
package main
import (
"net/http"
)
func main() {
h := http.FileServer(http.Dir("~"))
http.ListenAndServe(":8080", h)
}
- Python版本
直接在命令行上写入
$python3 -m http.server 8080
对于Python2
python -m SocketServer 8080
如果非要写代码实现也不难。
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingTCPServer
class ThreadingHTTPServer(ThreadingTCPServer, HTTPServer):
pass
if __name__ == '__main__':
httpd = ThreadingHTTPServer(('', 8080), BaseHTTPRequestHandler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nKeyboard interrupt received, exiting.")
sys.exit(0)
如果你喜欢,可以把代码中的ThreadingTCPServer
改为ThreadingMixIn
。根据socketserver
源码不难理解,这也更符合编程规范。
使用epoll实现的I/O复用简易HTTP服务器
由于Windows并没有epoll,所以该代码只能在Linux OS下运行。该代码示例用于理解I/O多路复用编程。关于epoll参考Linux I/O模型
import time
import socket
import select
default = True
if default:
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 2017 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) #TCP_NODELAY
server.bind(('', 8080))
server.listen(1)
server.setblocking(0)
epoll = select.epoll()
epoll.register(server.fileno(), select.EPOLLIN) #select.EPOLLIN | select.EPOLLET
try:
connections = {} #{fd: sock}
requests = {} #{fd: requests}
responses = {} #{fd: resposne}
while True:
events = epoll.poll(timeout=10) #[(fd, event), ...]
for fileno, event in events:
if fileno == server.fileno():
connection, address = server.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.POLLIN:
requests[fileno] += connections[fileno].recv(1024)
if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
epoll.modify(fileno, select.EPOLLOUT) #修改监听文件描述符的方式,修改文件描述符的事件
print('-'*40, requests[fileno].decode()[:-2], sep='\n')
elif event & select.EPOLLOUT:
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
if len(responses[fileno]) == 0:
epoll.modify(fileno, 0) #
connections[fileno].shutdown(socket.SHUT_RDWR)
elif event & select.EPOLLHUP:
#Hang up happened on the assoc. fd
epoll.unregister(fileno)
connections[fileno].close()
del connections[fileno]
finally:
epoll.unregister(server.fileno())
epoll.close()
server.close()
select模块中poll
的用法类似,但底层原理是不一样的。
转载请包括本文地址:https://allenwind.github.io/blog/2622
更多文章请参考:https://allenwind.github.io/blog/archives/
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK