Просмотр сокетов с помощью Glib в Windows переводит их в неблокирующий режим - PullRequest
3 голосов
/ 30 октября 2009

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

Ответы [ 4 ]

1 голос
/ 31 октября 2010

GIO содержит GSocket , «объект сетевого сокета низкого уровня» начиная с 2.22. Однако это еще предстоит перенести на pygobject в Windows .

1 голос
/ 14 ноября 2009

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

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

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

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

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

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

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

0 голосов
/ 09 мая 2011

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

0 голосов
/ 19 ноября 2009

Я не уверен, поможет ли это (я не разбираюсь в функции poll или сокетах 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);
...