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

相關文章
相關標籤/搜索