#!/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,分别拿到 文件描述号 和对应的事件 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()