Python下的I/O多路复用

1.什么是I/O多路复用

  I/O多路复用技术经过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的状况下能够同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优点是系统开销小,系统不须要建立新的额外进程或者线程,也不须要维护这些进程和线程的运行,降底了系统的维护工做量,节省了系统资源,I/O多路复用的主要应用场景以下:html

  • 服务器须要同时处理多个处于监听状态或者多个链接状态的套接字python

  • 服务器须要同时处理多种网络协议的套接字数组

2.select和epoll

  在Python中的异步I/O的基础就是 select 模块的 select 函数,服务器

  select [IO多路复用的机制]网络

  # select每次遍历都须要把fd集合从用户态拷贝到内核态,开销较大,受系统限制最大1024
  select.select(rlist, wlist, xlist[, timeout])
  # poll和select很像 经过一个pollfd数组向内核传递须要关注的事件,没有描述符1024限制
  select.poll()
  # 建立epoll句柄,注册监听事件,经过回调函数等待事件产生,不作主动扫描,整个过程对fd只作一次拷贝.打开最大文件数后,不受限制,1GB内存大约是10万连接
  select.epoll([sizehint=-1])多线程

  select.epollapp

    EPOLLIN # 监听可读事件
    EPOLLET # 高速边缘触发模式,即触发后不会再次触发直到新接收数据
    EPOLLOUT # 监听写事件异步

    epoll.poll([timeout=-1[, maxevents=-1]]) # 等待事件,未指定超时时间[毫秒]则为一直阻塞等待
    epoll.register(fd,EPOLLIN) # 向epoll句柄中注册,新来socket连接,监听可读事件
    epoll.modify(fd, EPOLLET | EPOLLOUT) # 改变监听事件为边缘触发,监听写事件
    epoll.fileno() # 经过连接对象获得fd
    epoll.unregister(fd) # 取消fd监听事件socket

 select 

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket
import select

s = socket.socket()  # 建立一个对象
s.bind(("127.0.0.1", 8888))  # 须要监听的地址和端口(传入参数是一个元组)
s.listen(5)
inputs = [s]  # 监听队列
while True:
    rs, ws, es = select.select(inputs, [], [])
    for r in rs:
        c, addr = s.accept()  # 获取客户端链接的信息
        print "Client from:", addr
        inputs.append(c)  # 把客户端加入监听队列
    else:
        try:
            data = r.recv(1024)
            disconnected = not data
        except socket.error:  # 出现socker错误把 disconnected设置为True
            disconnected = True
        if disconnected:  # 若是disconnected结果为True
            print r.getpeername(), "disconnected"
            inputs.remove(r)  # 移除监听
        else:
            print data
简单的select服务端
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import socket
 4 
 5 c = socket.socket()
 6 c.connect(('127.0.0.1', 8888))
 7 
 8 while True:
 9     inp = raw_input('please input:')
10     c.sendall(inp)
11 c.close()
简单 的客户端

参考资料:python基础教程(第二版)ide

epoll

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket, select

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!'
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 8888))
s.listen(1)
s.setblocking(0)
epoll = select.epoll()
epoll.register(s.fileno(), select.EPOLLIN)
try:
    connections = {}
    requests = {}
    responses = {}
    while True:
        events = epoll.poll(1)
        for fileno, event in events:
            if fileno == s.fileno():
                connection, address = s.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:
                requests[fileno] += connections[fileno].recv(1024)
                if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
                    epoll.modify(fileno, select.EPOLLOUT)
                    connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1)
                    print('-' * 40 + '\n' + requests[fileno].decode()[:-2])
            elif event & select.EPOLLOUT:
                byteswritten = connections[fileno].send(responses[fileno])
                responses[fileno] = responses[fileno][byteswritten:]
                if len(responses[fileno]) == 0:
                    connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 0)
                    epoll.modify(fileno, 0)
                    connections[fileno].shutdown(socket.SHUT_RDWR)
            elif event & select.EPOLLHUP:
                epoll.unregister(fileno)
                connections[fileno].close()
                del connections[fileno]
finally:
    epoll.unregister(s.fileno())
epoll

 参考资料 http://scotdoyle.com/python-epoll-howto.html

相关文章
相关标签/搜索