Assistindo soquetes com Glib no Windows coloca-los no modo sem bloqueio
Pergunta
O código a seguir não funciona corretamente no Windows (mas não em 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)
Snippets de comentários em vários lugares na fonte glib, e outros lugares mencionar que no Windows, tomadas são colocadas em modo não-bloqueio durante a votação. Como resultado, o self.outgoing_cb
callback é constantemente chamado, e escrita para o socket falhar com esta mensagem de erro:
[Errno 10035] A non-blocking socket operation could not be completed immediately
Chamando sock.setblocking(True)
antes de escrever não parece contornar isso. Ao baixar a prioridade da votação, e ignorando a mensagem de erro, ele funciona como esperado, mas joga muito longe para muitos eventos, e consome muita CPU. Existe uma maneira de contornar esta limitação no Windows?
Atualizar
Eu poderia salientar, que todo o ponto de pesquisa para POLLOUT
é que quando você faz a chamada de escrita que você não terá EAGAIN
/ EWOULDBLOCK
. A mensagem de erro estranho que eu estou recebendo, eu acredito que seria o equivalente do Windows desses códigos de erro 2. Em outras palavras, eu estou recebendo eventos gobject.IO_OUT
quando o soquete não me deixa escrever com sucesso, e colocá-lo em modo de bloqueio ainda me dá esse erro inadequada.
Outra atualização
No Linux, onde esta funciona corretamente, o soquete não está no modo sem bloqueio, e recebo IO_OUT
, quando o socket vai me deixar escrever sem bloqueio ou jogando um erro. É essa funcionalidade Quero melhor emular / restauração no Windows.
Outras notas
De 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.
De 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.
Solução
Existe um problema com fazendo non-blocking I / O? Parece meio estranho usar loops de votação se você estiver usando o bloqueio I / O.
Quando eu escrevo programas como este que tendem a fazer o seguinte:
-
Tampão os bytes que deseja enviar para o descritor de arquivo.
-
Apenas pedir
IO_OUT
(ou o equivalentepoll()
,POLLOUT
) eventos quando o referido tampão é não-vazia. -
Quando
poll()
(ou equivalente) tem sinalizado que você está pronto para escrever, emitir a gravação. Se você receberEAGAIN
/EWOULDBLOCK
, remover os bytes que você escreveu com sucesso a partir do buffer e espera para a próxima vez que você se sinalizado. Se você escreveu com sucesso todo o buffer, em seguida, parar de pedirPOLLOUT
para que você não spuriously acordar.
(Meu palpite é que as ligações Win32 estiver usando WSAEventSelect e WaitForMultipleObjects()
para simular poll()
, mas o resultado é o mesmo ...)
Eu não tenho certeza como a sua abordagem desejada com soquetes bloqueio iria funcionar. Você está "acordar" constantemente porque você pediu para acordá-lo quando você pode escrever. Você só quer especificar que quando você tem dados para escrever ... Mas então, quando você acorda, o sistema não vai realmente dizer-lhe quanto de dados você pode escrever sem bloqueio, de modo que é uma boa razão para usar non-blocking I / O.
Outras dicas
GIO contém GSocket , um "socket objeto de rede de baixo nível" desde 2,22. No entanto, este ainda está para ser portado para pygobject no Windows .
Eu não tenho certeza se isso ajuda (não estou acostumado com a função de pesquisa ou os soquetes MFC e não sei o polling é uma exigência da sua estrutura de programa), então tome isso com um grão de sal:
Mas, para evitar um bloqueio ou EAGAIN na escrita, usamos escolha, ou seja, adicionar o soquete para o conjunto de escrita que é passado para selecionar, e se select () volta com rc = 0 a tomada aceitará gravações imediatamente. ..
O ciclo de escrita que usamos em nosso aplicativo é (em pseudocódigo):
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);
Você pode usar torcida , que inclui suporte para GTK (mesmo no Windows) e irá lidar com todo o várias condições de erro que sem bloqueio soquetes no Windows gostaria de levantar.