WindowsでGlibを​​使用してソケットを監視すると、ソケットが非ブロックモードになります

StackOverflow https://stackoverflow.com/questions/1648631

質問

次のコードはWindowsでは正常に動作しません(Linuxでは動作します):

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(True)
    sock.connect(address)
    gobject.io_add_watch(
            sock.fileno(),
            gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP,
            callback)

glibソースのさまざまな場所にあるコメントのスニペット、および他の場所では、Windowsではポーリング中にソケットが非ブロックモードにされることが記載されています。その結果、コールバック self.outgoing_cb が常に呼び出され、次のエラーメッセージでソケットへの書き込みが失敗します。

[Errno 10035] A non-blocking socket operation could not be completed immediately

書き込み前に sock.setblocking(True)を呼び出しても、これを回避できないようです。ポーリングの優先度を下げて、エラーメッセージを無視することで、期待どおりに動作しますが、多くのイベントにはるかにスローされ、大量のCPUを消費します。 Windowsでこの制限を回避する方法はありますか?

更新

指摘するかもしれませんが、 POLLOUT のポーリングの全体のポイントは、書き込み呼び出しを行っても EAGAIN / EWOULDBLOCK 。私が得ている奇妙なエラーメッセージは、これらの2つのエラーコードに相当するWindowsだと思います。つまり、ソケットが正常に書き込みを行えないときに gobject.IO_OUT イベントが発生し、 ブロッキングモードにすると、この不適切なエラーが発生します。

別の更新

これが正常に機能するLinuxでは、ソケットは非ブロックモードに切り替えられず、ソケットがブロックせずにエラーをスローしたり、エラーをスローしたりするときに IO_OUT を受け取ります。 Windowsでエミュレート/復元したいのはこの機能です。

その他のメモ

man poll から:

   poll()  performs a similar task to select(2): it waits for one of a set
   of file descriptors to become ready to perform I/O.
          POLLOUT
                 Writing now will not block.

man select から:

A file descriptor  is considered ready if it is possible to perform the corre‐
sponding I/O operation (e.g., read(2)) without blocking.
役に立ちましたか?

解決

非ブロッキングI / Oの実行に問題はありますか?ブロッキングI / Oを使用している場合、ポーリングループを使用するのは奇妙に思えます。

このようなプログラムを書くとき、私は次のことをする傾向があります:

  • ファイル記述子に送信するバイトをバッファリングします。

  • バッファが空でない場合にのみ、 IO_OUT (または poll()相当、 POLLOUT )イベントのみを要求します。

  • poll()(または同等のもの)が書き込み準備ができていることを通知したら、書き込みを発行します。 EAGAIN / EWOULDBLOCK を受け取った場合は、正常に書き込んだバイトをバッファーから削除し、次に信号が送られるのを待ちます。バッファ全体の書き込みに成功したら、 POLLOUT の要求を停止して、誤って起動しないようにします。

(Win32バインディングは WSAEventSelect および WaitForMultipleObjects() poll()をシミュレートしますが、結果は同じです...)

ソケットをブロックするための望ましいアプローチがどのように機能するかわかりません。 「目覚めています」書くことができるときに目を覚ますように頼んだからです。書き込むデータがある場合にのみ指定する必要があります...しかし、その後、システムが目を覚ますと、ブロックせずに書き込むことができるデータの量をシステムが実際に通知しないので、ノンブロッキングI / Oを使用する正当な理由。

他のヒント

GIO には GSocket 、「低レベルネットワークソケットオブジェクト」 2.22以降ただし、これはまだ Windowsのpygobject に移植されていません。

これが役立つかどうかわかりません(ポーリング機能やMFCソケットに精通しておらず、ポーリングがプログラム構造の要件であることを知りません)。

ただし、書き込み時のブロッキングやEAGAINを避けるために、selectを使用します。つまり、selectに渡される書き込みセットにソケットを追加し、rc = 0でselect()が返された場合、ソケットはすぐに書き込みを受け入れます。 ..

アプリで使用する書き込みループは(擬似コードで)です:

set_nonblocking.
count= 0.
do {
   FDSET writefds;
   add skt to writefds.
   call select with writefds and a reaonsable timeout.
   if (select fails with timeout) {
       die with some error;
   } 

   howmany= send(skt, buf+count, total-count).
   if (howmany>0) {
       count+= howmany.
   }
} while (howmany>0 && count<total);

Twisted を使用できます。これには GTKのサポート(Windowsでも)およびすべてのさまざまなエラー条件は、Windowsの非ブロッキングソケットが発生することを好む。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top