Просмотр сокетов с Glib в Windows переводит их в неблокирующий режим

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) предшествующее написание, похоже, не позволяет обойти это.Понижая приоритет опроса и игнорируя сообщение об ошибке, он работает так, как ожидалось, но приводит ко многим событиям и потребляет много ресурсов процессора.Есть ли способ обойти это ограничение в Windows?

Обновить

Я мог бы отметить, что весь смысл опроса для POLLOUT это значит, что когда вы делаете запрос на запись, вы не получаете EAGAIN/EWOULDBLOCK.Странное сообщение об ошибке, которое я получаю, я полагаю, будет эквивалентом Windows этих 2 кодов ошибок.Другими словами, я получаю 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.
Это было полезно?

Решение

Есть ли проблема с выполнением неблокирующего ввода-вывода?Кажется немного странным использовать циклы опроса, если вы используете блокирующий ввод-вывод.

Когда я пишу подобные программы, я обычно делаю следующее:

  • Буферизуйте байты, которые я хочу отправить, в файловый дескриптор.

  • Только попроси IO_OUT (или тот poll() эквивалент, POLLOUT) события, когда указанный буфер непустой.

  • Когда poll() (или эквивалент) сигнализировал, что вы готовы писать, выполните команду write.Если вы получите EAGAIN/EWOULDBLOCK, удалите байты, которые вы успешно записали, из буфера и дождитесь следующего сигнала.Если вы успешно записали весь буфер, то прекратите запрашивать POLLOUT чтобы ты не проснулся ложным образом.

(Я предполагаю, что привязки Win32 используют Выбор WSAEventSelect и WaitForMultipleObjects() для имитации poll(), но результат тот же ...)

Я не уверен, как будет работать ваш желаемый подход с блокирующими сокетами.Вы постоянно "просыпаетесь", потому что попросили разбудить вас, когда сможете писать.Вы хотите указать это только тогда, когда у вас есть данные для записи...Но потом, когда это разбудит вас, система на самом деле ничего вам не скажет сколько данные, которые вы можете записывать без блокировки, так что это хорошая причина использовать неблокирующий ввод-вывод.

Другие советы

ДЖИО содержит GSocket ( Сокет), "низкоуровневый объект сетевого сокета" начиная с 2.22.Однако это еще предстоит перенести на pygobject в Windows.

Я не уверен, помогает ли это (я не разбираюсь в функции опроса или сокетах MFC и не знаю, что опрос является требованием вашей структуры программы), поэтому отнеситесь к этому с недоверием:

Но чтобы избежать блокировки или EAGAIN при записи, мы используем select, т.е.добавьте сокет к набору записи, который передается в select, и если функция select() вернет значение rc=0, сокет сразу же примет запись ...

Цикл записи, который мы используем в нашем приложении, является (в псевдокоде):

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);

Вы могли бы использовать Скрученный, который включает в себя поддержка GTK (даже в Windows) и будет обрабатывать все различные условия ошибки, которые любят вызывать неблокирующие сокеты в Windows.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top