Asyncore モジュールは、ソケット サービスのクライアントとサーバーを非同期で作成するためのインフラストラクチャを提供します。
プログラムが単一のプロセッサ上で「同時に複数のことを実行する」には 2 つの方法しかありません。マルチスレッド プログラミングは最もシンプルで最も一般的な方法ですが、実際にマルチスレッドを使用せずにマルチスレッドのほぼすべての利点を維持できるようにする別の非常に異なる手法があります。 プログラムが主に I/O バウンドである場合、これが唯一の方法です。プログラムがプロセッサーに依存している場合、スレッドをプリエンプティブにスケジュールすることが本当に必要である可能性があります。ただし、プロセッサに依存する Web サーバーはほとんどありません。
オペレーティング システムが I/O ライブラリで select() システム コールをサポートしている場合 (ほぼすべてのシステムがサポートしています)、I/O が他の通信を行うときに、それを使用して複数の通信チャネルを同時に処理できます。バックグラウンドがビジー状態のときに作業します。この戦略は、特に最初は奇妙で複雑に見えるかもしれませんが、多くの点でマルチスレッド プログラミングよりも理解し、制御するのが簡単です。 asyncore モジュールは多くの問題を解決し、複雑で高性能なネットワーク サーバーとクライアントを迅速に構築できるようにします。会話型アプリケーションおよびプロトコルの場合、asynchat モジュールは非常に便利です。
これら 2 つのモジュールの背後にある考え方は、1 つ以上のネットワーク チャネルと、asyncore.dispatcher クラスと asynchat.async_chat クラスのインスタンスを作成することです。独自のマッピングを提供しない場合、チャネルの作成にはこれら 2 つが使用されます。インスタンスは、loop() 関数によって使用されるグローバル マップに追加されます。
最初のチャネルが作成されたら、loop() 関数を呼び出すとチャネル サービスがアクティブ化され、最後のチャネル (非同期サービスでマップに追加されたすべてのチャネルを含む) が閉じられるまで継続されます。
このモジュール ファイルには、loop() 関数とディスパッチャー基本クラスが含まれています。loop() 関数はグローバル関数であり、チャネルとも呼ばれるディスパッチャー インスタンスを保持する辞書をチェックします。
ディスパッチャ クラスを継承するすべてのオブジェクトは、処理が必要なソケットと見なすことができるため、これを使用する場合は、ディスパッチャを継承するクラスを定義し、いくつかのメソッド (通常は handle_ で始まるメソッド) を書き換えるだけで済みます。 。
ポート転送の例
プログラムが同時に 1 つのことを実行したい場合、マルチスレッドが最も速く最も一般的な方法ですが、別の方法もあります。この方法は次のとおりです。 I/O トラフィックが大きい場合に特に役立ちます。オペレーティング システムが選択機能をサポートしている場合は、バックグラウンドで I/O の読み取りおよび書き込みを行うことができます。このモジュールは複雑に思えますが、実際には理解する方法がたくさんあり、このドキュメントはそれらを解決するのに役立ちます。
このモジュールは、C++ のイベント選択モデルに似た、イベント駆動型の非同期 I/O であるべきだと思います。読み取りまたは書き込みイベントが発生するたびに、書き直したイベント関数によって処理されます。
asyncore モジュールを使用して書かれたポート転送スクリプトがあります。このスクリプトから、asyncore の基本的な使用法の概要を得ることができます。
この記事では、クライアントは私たちのコンピュータであり、サーバーは転送先のアドレスです。つまり、クライアントからこのスクリプトに送信された情報は、このスクリプトによってサーバーに転送されます。
まず、フォワーダー クラスを定義します。
class forwarder(asyncore.dispatcher): def __init__(self, ip, port, remoteip,remoteport,backlog=5): asyncore.dispatcher.__init__(self) self.remoteip=remoteip self.remoteport=remoteport self.create_socket(socket.AF_INET,socket.SOCK_STREAM) self.set_reuse_addr() self.bind((ip,port)) self.listen(backlog) def handle_accept(self): conn, addr = self.accept() # print '--- Connect --- ' sender(receiver(conn),self.remoteip,self.remoteport)
このクラスは、asyncore モジュールのディスパッチャー クラスを継承します (これは私たちのメインです)このクラスには、後でオーバーロードされるいくつかの関数が含まれています)、コンストラクターは 5 つのパラメーターを取得します。1 番目と 2 番目のパラメーターはスクリプトが監視するローカル IP とポート、3 番目と 4 番目のパラメーターはサーバーの IP とポートです。 5 番目のパラメータは listen 関数のパラメータで、待機キューの最大長です。
このクラスを使用するには、次のように新しいオブジェクトを作成し、対応する IP とポートを渡してループに入るだけです:
forwarder(options.local_ip,options.local_port,options.remote_ip,options.remote_port) asyncore.loop()
ループに入った後は、バックグラウンドで実行されているデーモン スレッドを開始して、ソケット イベントが発生するのを待つのと同じです。
私たちのスクリプトはポート転送ツールであるため、実際に実行されるプロセスは次のとおりです。クライアントはこのスクリプトのポートに接続し、このポートに送信されたデータ スクリプトはサーバーのアドレスとポートに自動的に転送されます。したがって、最初に受信するのは接続メッセージ (accept イベント) である必要があります。
その後、accept イベントが発生すると、handle_accept 関数に入ります。したがって、handle_accept 関数が実際に accept 関数を呼び出して、クライアント接続オブジェクトとアドレスを受信することがわかります。取得後、新しい送信者クラス オブジェクトが作成されます。このオブジェクトは次のように定義されます。
这个类也是继承自asyncore.dispatcher,它的构造函数接收3个参数,分别是recv对象(这个之后说到),远端地址,对应端口。
函数中又新建了一个socket,这个socket就是和服务端端口通信的socket,然后调用connect连接这个端口。
之后其实也是进入了一个等待消息的过程,因为我们发送了一个connect,所以下一次接收到的消息应该是connect,而handle_connect是一个pass掉的函数。没有执行任何内容。
在连接完成后,我们就相当于建立好了一个端口转发的通道。当客户端向这个脚本监听的端口发送数据包时,它就会自动转发到服务端端口上。服务端端口返回的数据包,会自动转发到客户端上。
回到构造函数的第1个参数,我们在forwarder类函数中可以看到,传入的是一个receiver(conn)对象,receiver也是一个类,我们来看看这个类的定义:
class receiver(asyncore.dispatcher): def __init__(self,conn): asyncore.dispatcher.__init__(self,conn) self.from_remote_buffer='' self.to_remote_buffer='' self.sender=None def handle_connect(self): pass def handle_read(self): read = self.recv(4096) # print '%04i -->'%len(read) self.from_remote_buffer += read def writable(self): return (len(self.to_remote_buffer) > 0) def handle_write(self): sent = self.send(self.to_remote_buffer) # print '%04i <--'%sent self.to_remote_buffer = self.to_remote_buffer[sent:] def handle_close(self): self.close() if self.sender: self.sender.close()
它也是继承了asyncore.dispatcher,构造函数只接收一个参数,就是connect的返回值,一个连接对象。
实际上这个对象它就是监听、处理与客户端的通信,而之前说的sender对象是监听、处理与服务端的通信。
以上就是Python的Asyncore异步Socket模块及实现端口转发的例子的内容,更多相关内容请关注PHP中文网(www.php.cn)!