WindowsでGlibを使用してソケットを監視すると、ソケットが非ブロックモードになります
質問
次のコードは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の非ブロッキングソケットが発生することを好む。