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 = 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!'
- # 创建一个服务端的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,分别拿到 文件描述号 和对应的事件
- forfileno,event in events:
- # 如果当前的文件描述号是serversocket,那么说明有新的连接
- iffileno == 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)
- # 判断换行 和 两个换行是否在接收过来的数据中
- ifEOL1inrequests[fileno] or EOL2in 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
- iflen(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 服务器源码分析
来源: http://www.bubuko.com/infodetail-2078573.html