Eu tenho um pequeno programa de servidor que aceita conexões em um soquete TCP ou UNIX local, lê um comando simples e, dependendo do comando, envia uma resposta. O problema é que o cliente pode não ter interesse na resposta algumas vezes e sai mais cedo; portanto, gravar nesse soquete causará um SIGPIPE e causará uma falha no servidor. Qual é a melhor prática para evitar a falha aqui? Existe uma maneira de verificar se o outro lado da linha ainda está lendo? (select () parece não funcionar aqui, pois sempre diz que o soquete é gravável). Ou devo apenas pegar o SIGPIPE com um manipulador e ignorá-lo?
Outro método é alterar o soquete para que nunca gere SIGPIPE em write (). Isso é mais conveniente em bibliotecas, nas quais você pode não querer um manipulador de sinal global para o SIGPIPE.
Na maioria dos sistemas baseados em BSD (MacOS, FreeBSD ...), (supondo que você esteja usando C / C ++), você pode fazer isso com:
Com isso, em vez de o sinal SIGPIPE ser gerado, o EPIPE será retornado.
fonte
Estou muito atrasado para a festa, mas
SO_NOSIGPIPE
não é portátil e pode não funcionar no seu sistema (parece ser uma coisa do BSD).Uma boa alternativa se você estiver, digamos, em um sistema Linux sem,
SO_NOSIGPIPE
seria definir oMSG_NOSIGNAL
sinalizador na sua chamada send (2).Exemplo de substituição
write(...)
porsend(...,MSG_NOSIGNAL)
(consulte o comentário de nobar )fonte
QTcpSocket
objeto Qt para quebrar o uso de soquetes, tive que substituir awrite
chamada de método por um sistema operacionalsend
(usando osocketDescriptor
método). Alguém conhece uma opção mais limpa para definir essa opção em umaQTcpSocket
classe?Neste post , descrevi uma possível solução para o caso Solaris quando nem SO_NOSIGPIPE nem MSG_NOSIGNAL estão disponíveis.
Código de exemplo em https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156
fonte
Manipular SIGPIPE localmente
Geralmente, é melhor lidar com o erro localmente, em vez de em um manipulador de eventos de sinal global, pois localmente você terá mais contexto sobre o que está acontecendo e que recurso tomar.
Eu tenho uma camada de comunicação em um dos meus aplicativos que permite que ele se comunique com um acessório externo. Quando ocorre um erro de gravação, lanço uma exceção na camada de comunicação e deixo borbulhar até um bloco try catch para lidar com isso.
Código:
O código para ignorar um sinal SIGPIPE para que você possa manipulá-lo localmente é:
Esse código impedirá que o sinal SIGPIPE seja gerado, mas você receberá um erro de leitura / gravação ao tentar usar o soquete, portanto, será necessário verificar isso.
fonte
Você não pode impedir que o processo na extremidade oposta de um pipe saia e, se ele sair antes de terminar a gravação, você receberá um sinal SIGPIPE. Se você assinar o sinal, sua gravação retornará com um erro - e você precisará observar e reagir a esse erro. Apenas capturar e ignorar o sinal em um manipulador não é uma boa idéia - você deve observar que o canal agora está desativado e modifica o comportamento do programa para que ele não grave novamente no canal (porque o sinal será gerado novamente e ignorado e tente novamente, e todo o processo pode durar muito tempo e desperdiçar muita energia da CPU).
fonte
Eu acredito que está certo. Você quer saber quando a outra extremidade fechou o descritor e é isso que o SIGPIPE diz.
Sam
fonte
Manual do Linux disse:
Mas para o Ubuntu 12.04 não está certo. Eu escrevi um teste para esse caso e sempre recebo o EPIPE sem o SIGPIPE. SIGPIPE é gerado se eu tentar escrever no mesmo soquete quebrado pela segunda vez. Portanto, você não precisa ignorar o SIGPIPE. Se esse sinal ocorrer, significa erro lógico no seu programa.
fonte
Desative o sigpipes de acordo com todos ou capture e ignore o erro.
Sim, use select ().
Você precisa selecionar os bits de leitura . Você provavelmente pode ignorar os bits de gravação .
Quando a extremidade remota fecha seu identificador de arquivo, selecione informará que existem dados prontos para leitura. Ao ler isso, você receberá 0 bytes, e é assim que o sistema operacional informa que o identificador do arquivo foi fechado.
O único momento em que você não pode ignorar os bits de gravação é se estiver enviando grandes volumes, e existe o risco de a outra extremidade ficar acumulada, o que pode fazer com que seus buffers sejam preenchidos. Se isso acontecer, tentar gravar no identificador de arquivo pode causar um bloqueio ou falha no seu programa / thread. O teste de seleção antes da gravação o protegerá disso, mas não garante que o outro lado esteja íntegro ou que seus dados cheguem.
Observe que você pode obter um sigpipe em close (), assim como quando você escreve.
Fechar libera todos os dados em buffer. Se a outra extremidade já estiver fechada, o fechamento falhará e você receberá um sinal.
Se você estiver usando TCPIP em buffer, uma gravação bem-sucedida significa que seus dados foram colocados na fila para envio, não significa que foram enviados. Até você fechar com êxito, você não sabe que seus dados foram enviados.
Sigpipe diz que algo deu errado, não diz o que ou o que você deve fazer sobre isso.
fonte
Sob um sistema POSIX moderno (ou seja, Linux), você pode usar a
sigprocmask()
funçãoSe você deseja restaurar o estado anterior posteriormente, salve o local
old_state
seguro. Se você chamar essa função várias vezes, precisará usar uma pilha ou salvar apenas o primeiro ou o últimoold_state
... ou talvez ter uma função que remova um sinal bloqueado específico.Para mais informações, leia a página de manual .
fonte
sigprocmask
adiciona ao conjunto de sinais bloqueados, para que você possa fazer tudo isso com uma única chamada.old_state
para que possa restaurá-lo mais tarde, se assim o desejar. Se você sabe que nunca precisará restaurar o estado, de fato não há necessidade de ler e armazenar dessa maneira.sigprocmask()
lê o estado antigo, a chamada parasigaddset
modificá-la, a segunda chamada parasigprocmask()
escreve. Você pode remover para a primeira chamada, inicializar comsigemptyset(&set)
e alterar a segunda chamada parasigprocmask(SIG_BLOCK, &set, &old_state)
.