ホームページ  >  記事  >  バックエンド開発  >  IO多重化を学ぶPythonネットワークプログラミングに関するepollの紹介

IO多重化を学ぶPythonネットワークプログラミングに関するepollの紹介

高洛峰
高洛峰オリジナル
2017-03-19 14:53:131629ブラウズ

epollとは

epollとは何ですか? linux ネットワーク プログラミングでは、長い間、select がイベントのトリガーに使用されてきました。新しい Linux カーネルには、epoll というこれに代わるメカニズムがあります。もちろん、これは 2.6 カーネルに固有のものではありません。2.5.44 カーネルで導入されました (epoll(4) は、Linux カーネル 2.5.44 で導入された 新しい API です)。あらゆる利点を備え、Linux2.6 で最もパフォーマンスの高い多重化 I/O 準備完了通知方法として認識されています。

select と比較した場合、epoll の最大の利点は、監視 fd の数が増加しても効率が低下しないことです。カーネル内の選択実装ではポーリングによって処理されるため、ポーリングされる fds の数が増えるほど時間がかかります。

Epoll は機能します

epoll は、準備ができたファイル記述子のみを通知します。また、準備ができたファイル記述子を取得するために epoll_wait() を呼び出すと、実際の記述子ではなく、準備完了の記述子の数を表す値が返されます。ここでは、epoll で指定された

array に移動して、対応する数のファイル記述子を順番に取得する必要があります。これにより、システム呼び出し中にこれらのファイル記述子をコピーする必要が完全になくなります。

もう 1 つの重要な改善点は、epoll がイベントベースの準備完了通知メソッドを採用していることです。 select/poll では、カーネルはプロセスが特定のメソッドを呼び出した後でのみ、監視されているすべてのファイル記述子をスキャンします。一方、epoll は、事前に epoll_ctl() を通じてファイル記述子を登録します。特定のファイル記述子の準備が整うと、カーネルはコールバック メカニズムを使用します。このファイル記述子を迅速にアクティブにするコールバックに似ており、プロセスが epoll_wait() を呼び出したときに通知されます。


上記からわかるように、epoll は select モデルと Poll

モデル を改良したもので、ネットワーク プログラミングのパフォーマンスを向上させ、大規模な同時リクエスト用の C/S アーキテクチャ で広く使用されています。

Python での epoll

1. トリガー方法: エッジ トリガー/水平トリガー、Unix/Linux オペレーティング システムにのみ適用

2. 一般的な手順

3. epoll

オブジェクトを作成します。

——epollIO多重化を学ぶPythonネットワークプログラミングに関するepollの紹介 オブジェクトを作成します

特定のソケット上の特定のイベントを監視するように epoll オブジェクトに指示します—特定のソケット上の特定のイベントを監視するように epoll オブジェクトに指示します

どのソケットで指定されたイベントが発生したのかを epoll オブジェクトに尋ねます最後のクエリ—最後のクエリ以降、どのソケットに指定されたイベントが発生したかを epoll オブジェクトに問い合わせますそれらのソケットに対していくつかの アクション

を実行します—これらのソケットに対していくつかの操作を実行します

epoll オブジェクトに

リストを変更するように指示します監視するソケットやイベントの

— 監視するソケットやイベントの

リスト を変更するように epoll オブジェクトに指示します — 完了するまでステップ 3 ~ 5 を繰り返します — 完了するまでステップ 3 ~ 5 を繰り返します

epoll オブジェクトを破棄します—epoll オブジェクトを破棄します

4. 関連する使用法

import select Import select module

epoll = select.epoll() epoll オブジェクトを作成します

epoll.register(ファイル ハンドル, イベント タイプ) ファイルを登録します監視対象のハンドルとイベント

イベントタイプ:

select.EPOLLIN 読み取り可能なイベント

select.EPOLLOUT 書き込み可能なイベント

select.EPOLLERR エラーイベント

select.EPOLLHUP クライアント切断イベント

UEpoll.unregister (ファイルハンドル) を破棄します。ファイル ハンドル

Epoll.Poll (タイムアウト) ファイル ハンドルが変更されると、リストの形式でユーザー プロセスにアクティブに報告されます。タイムアウト

はタイムアウト時間です。デフォルトは -1、つまりデフォルトですは -1、つまり、デフォルトは -1、つまり、デフォルトは -1、つまり、デフォルトは -1、つまり、デフォルトは -1、つまり、デフォルトは -1、つまりつまり、デフォルトは -1 です。つまり、現在のファイル ハンドルの変更を 1 秒ごとに報告するように指定されている場合、デフォルトはファイル ハンドルが変更されるまで待機します。は変化がありません。空の

epoll 制御ファイル記述子を返します)

epoll.modfiy(fineno,event)fineno はファイル記述子であり、event はその機能です。ファイル記述子に対応するイベントを変更します

epoll.fromfd(fileno) 指定されたファイル記述子から epoll オブジェクトを作成します

epoll.close() epoll オブジェクトの制御ファイル記述子を閉じます

5 例: クライアントは、サーバーが受信するデータを送信します クライアントに戻ります

サーバーコード

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
import select
import Queue
#创建socket对象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#设置IP地址复用
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#ip地址和端口号
server_address = ("127.0.0.1", 8888)
#绑定IP地址
serversocket.bind(server_address)
#监听,并设置最大连接数
serversocket.listen(10)
print  "服务器启动成功,监听IP:" , server_address
#服务端设置非阻塞
serversocket.setblocking(False)  
#超时时间
timeout = 10
#创建epoll事件对象,后续要监控的事件添加到其中
epoll = select.epoll()
#注册服务器监听fd到等待读事件集合
epoll.register(serversocket.fileno(), select.EPOLLIN)
#保存连接客户端消息的字典,格式为{}
message_queues = {}
#文件句柄到所对应对象的字典,格式为{句柄:对象}
fd_to_socket = {serversocket.fileno():serversocket,}
while True:
  print "等待活动连接......"
  #轮询注册的事件集合,返回值为[(文件句柄,对应的事件),(...),....]
  events = epoll.poll(timeout)
  if not events:
     print "epoll超时无活动连接,重新轮询......"
     continue
  print "有" , len(events), "个新事件,开始处理......"
  
  for fd, event in events:
     socket = fd_to_socket[fd]
     #如果活动socket为当前服务器socket,表示有新连接
     if socket == serversocket:
            connection, address = serversocket.accept()
            print "新连接:" , address
            #新连接socket设置为非阻塞
            connection.setblocking(False)
            #注册新连接fd到待读事件集合
            epoll.register(connection.fileno(), select.EPOLLIN)
            #把新连接的文件句柄以及对象保存到字典
            fd_to_socket[connection.fileno()] = connection
            #以新连接的对象为键值,值存储在队列中,保存每个连接的信息
            message_queues[connection]  = Queue.Queue()
     #关闭事件
     elif event & select.EPOLLHUP:
        print 'client close'
        #在epoll中注销客户端的文件句柄
        epoll.unregister(fd)
        #关闭客户端的文件句柄
        fd_to_socket[fd].close()
        #在字典中删除与已关闭客户端相关的信息
        del fd_to_socket[fd]
     #可读事件
     elif event & select.EPOLLIN:
        #接收数据
        data = socket.recv(1024)
        if data:
           print "收到数据:" , data , "客户端:" , socket.getpeername()
           #将数据放入对应客户端的字典
           message_queues[socket].put(data)
           #修改读取到消息的连接到等待写事件集合(即对应客户端收到消息后,再将其fd修改并加入写事件集合)
           epoll.modify(fd, select.EPOLLOUT)
     #可写事件
     elif event & select.EPOLLOUT:
        try:
           #从字典中获取对应客户端的信息
           msg = message_queues[socket].get_nowait()
        except Queue.Empty:
           print socket.getpeername() , " queue empty"
           #修改文件句柄为读事件
           epoll.modify(fd, select.EPOLLIN)
        else :
           print "发送数据:" , data , "客户端:" , socket.getpeername()
           #发送数据
           socket.send(msg)
#在epoll中注销服务端文件句柄
epoll.unregister(serversocket.fileno())
#关闭epoll
epoll.close()
#关闭服务器socket
serversocket.close()

クライアントコード:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
#创建客户端socket对象
clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#服务端IP地址和端口号元组
server_address = ('127.0.0.1',8888)
#客户端连接指定的IP地址和端口号
clientsocket.connect(server_address)
while True:
    #输入数据
    data = raw_input('please input:')
    #客户端发送数据
    clientsocket.sendall(data)
    #客户端接收数据
    server_data = clientsocket.recv(1024)
    print '客户端收到的数据:'server_data
    #关闭客户端socket
    clientsocket.close()


以上がIO多重化を学ぶPythonネットワークプログラミングに関するepollの紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。