Pergunta

Eu tenho um programa servidor pequeno que aceita conexões em um TCP ou socket 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 às vezes e sai cedo, assim que escrever para que o soquete irá causar um SIGPIPE e fazer o meu queda do servidor. Qual é a melhor prática para evitar o acidente aqui? Existe uma maneira de verificar se o outro lado da linha ainda está lendo? (Select () não parece para trabalhar aqui como sempre diz o soquete é gravável). Ou devo apenas pegar o SIGPIPE com um manipulador e ignorá-lo?

Foi útil?

Solução

Você geralmente quer ignorar o SIGPIPE e manipular o erro diretamente em seu código. Isso ocorre porque os manipuladores de sinal em C têm muitas restrições sobre o que eles podem fazer.

A maneira mais portátil para fazer isso é para definir o manipulador SIGPIPE para SIG_IGN. Isso irá evitar qualquer tomada ou tubo de gravação de causar um sinal SIGPIPE.

Para ignorar o sinal SIGPIPE, utilize o seguinte código:

signal(SIGPIPE, SIG_IGN);

Se você estiver usando a chamada send(), outra opção é usar a opção MSG_NOSIGNAL, que irá transformar o comportamento SIGPIPE fora em uma base por chamada. Note-se que nem todos os sistemas operacionais suportam a bandeira MSG_NOSIGNAL.

Por fim, você pode também querer considerar a bandeira tomada SO_SIGNOPIPE que pode ser definido com setsockopt() em alguns sistemas operacionais. Isso vai evitar que SIGPIPE de ser causada por escreve apenas para as tomadas Ele é definido em.

Outras dicas

Outro método consiste em mudar a tomada para que ele nunca gera SIGPIPE on write (). Este é mais conveniente em bibliotecas, onde você pode não querer um manipulador de sinal global para SIGPIPE.

Na maioria baseados em BSD (MacOS, ... FreeBSD) sistemas, (supondo que você estiver usando C / C ++), você pode fazer isso com:

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

Com isto em efeito, em vez do sinal SIGPIPE sendo gerado, EPIPE será devolvido.

Estou super atrasado para a festa, mas SO_NOSIGPIPE não é portátil, e pode não funcionar no seu sistema (que parece ser uma coisa BSD).

Uma boa alternativa se você estiver em, digamos, um sistema Linux sem SO_NOSIGPIPE seria para definir o sinalizador MSG_NOSIGNAL em seu send (2) chamada.

Exemplo substituindo write(...) por send(...,MSG_NOSIGNAL) (ver Nobar 's comentário)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );

Neste pós eu descrevi possível solução para Solaris caso quando nem SO_NOSIGPIPE nem MSG_NOSIGNAL está disponível.

Em vez disso, temos que SIGPIPE temporariamente suprimir na atual segmento que executa o código da biblioteca. Veja como fazer isso: para SIGPIPE suprimir nós primeiro verificar se ele está pendente. Se isso acontecer, isso significa que ele está bloqueado neste segmento, e nós temos que fazer nada. Se a biblioteca gera SIGPIPE adicional, será fundida com o pendente, e isso é um não-op. Se SIGPIPE não esteja pendente, em seguida, bloqueá-lo neste segmento, e também verificar se ele já foi bloqueado. Então somos livres para executar nossas gravações. Quando estamos a restaurar SIGPIPE ao seu estado original, fazemos o seguinte: Se SIGPIPE estava pendente originalmente, não fazemos nada. Caso contrário, verifique se ele está pendente agora. Se isso acontecer (o que significa que as acções têm gerado um ou mais SIGPIPEs), então vamos esperar por ele neste segmento, limpando assim o seu status pendente (para fazer isso usamos sigtimedwait () com tempo limite zero; isso é para evitar o bloqueio em um cenário onde o usuário mal-intencionado enviado SIGPIPE manualmente para todo um processo: neste caso, vamos vê-lo pendente, mas outro segmento pode lidar com isso antes que nós tivemos uma mudança para esperar por ele). Depois de limpar status pendente que SIGPIPE desbloqueio neste segmento, mas somente se ele não foi bloqueado originalmente.

código Exemplo em https://github.com/kroki /XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156

Handle SIGPIPE localmente

Geralmente é melhor para lidar com o erro localmente em vez de em um manipulador de eventos de sinal global desde localmente, você terá mais contexto sobre o que está acontecendo eo que o recurso a tomar.

Eu tenho uma camada de comunicação em um dos meus aplicativos que permite que meu aplicativo para se comunicar com um acessório externo. Quando um erro de escrita ocorre eu jogo e exceção na camada de comunicação e deixá-lo borbulhar até um bloco try catch para lidar com ele lá.

Código:

O código para ignorar um sinal SIGPIPE de modo que você pode manipulá-lo localmente é:

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

Este código vai impedir que o sinal SIGPIPE de ser levantada, mas você vai ter um erro de leitura / gravação ao tentar usar o soquete, assim você terá que verificar para isso.

Você não pode impedir que o processo na extremidade de um tubo de sair, e se ele sai antes que você tenha escrito terminado, você receberá um sinal SIGPIPE. Se você SIG_IGN o sinal, então a sua gravação irá retornar com um erro - e você precisa observar e reagir a esse erro. Apenas recuperar e ignorando o sinal em um manipulador não é uma boa idéia - você deve observar que o tubo é extinta e modificar o comportamento do programa para que ele não escrever para o tubo de novo (porque o sinal será gerado novamente, e ignorado novamente, e você vai tentar novamente, e todo o processo poderia continuar por um longo tempo e desperdiçar uma grande quantidade de energia da CPU).

Ou devo apenas pegar o SIGPIPE com um manipulador e ignorá-lo?

Acredito que fica à direita. Quer saber quando a outra extremidade fechou sua descritor e é isso que SIGPIPE lhe diz.

Sam

manual do Linux disse:

EPIPE A extremidade local foi desligado em uma conexão orientada socket. Neste caso, o processo também vai receber um SIGPIPE a menos MSG_NOSIGNAL está definido.

Mas para Ubuntu 12.04 não é certo. Eu escrevi um teste para esse caso e eu sempre receber EPIPE withot SIGPIPE. SIGPIPE é genereated se eu tentar escrever para o mesmo soquete quebrado segunda vez. Assim você não precisa ignorar SIGPIPE se este sinal acontece significa erro de lógica em seu programa.

Qual é a melhor prática para evitar o acidente aqui?

De qualquer desativar sigpipes como por todos, ou pegar e ignorar o erro.

Existe uma maneira de verificar se o outro lado da linha ainda está lendo?

Sim, o uso select ().

select () não parece trabalho aqui como sempre diz o soquete é gravável.

Você precisa selecionar as Leia pedaços. Você provavelmente pode ignorar o write pedaços.

Quando o extremo fecha sua alça de arquivo, selecione irá dizer-lhe que há dados pronto para ler. Quando você ir e ler isso, você vai voltar 0 bytes, que é como o sistema operacional informa que o identificador de arquivo foi fechada.

A única vez que você não pode ignorar os bits de escrita é se você está enviando grandes volumes, e há um risco da outra extremidade se backlogged, o que pode fazer com que seus buffers para preencher. Se isso acontecer, então a tentar escrever o identificador de arquivo pode causar o seu programa / thread para bloquear ou falhar. Teste escolha antes de escrever o protegerá de que, mas não garante que a outra extremidade é saudável ou que seus dados estão indo para chegar.

Note que você pode obter um SIGPIPE de close (), bem como quando você escreve.

Fechar libera quaisquer dados em buffer. Se a outra extremidade já foi fechado, em seguida, feche falhará, e você receberá um SIGPIPE.

Se você estiver usando tamponada TCPIP, então uma gravação bem-sucedida significa apenas que seus dados tenham sido colocados em fila para enviar, ele não significa que ele foi enviado. Até que você chamar com êxito perto, você não sabe que seus dados tenham sido enviados.

SIGPIPE diz-lhe alguma coisa errada se foi, não dizer o que, ou o que você deve fazer sobre ele.

De acordo com um moderno sistema POSIX (isto é, Linux), você pode usar a função sigprocmask().

#include <signal.h>

void block_signal(int signal_to_block /* i.e. SIGPIPE */ )
{
    sigset_t set;
    sigset_t old_state;

    // get the current state
    //
    sigprocmask(SIG_BLOCK, NULL, &old_state);

    // add signal_to_block to that existing state
    //
    set = old_state;
    sigaddset(&set, signal_to_block);

    // block that signal also
    //
    sigprocmask(SIG_BLOCK, &set, NULL);

    // ... deal with old_state if required ...
}

Se você quiser restaurar o estado anterior mais tarde, certifique-se de salvar o cofre old_state em algum lugar. Se você chamar essa função várias vezes, você precisa se quer usar uma pilha, ou apenas salvar o primeiro ou o último old_state ... ou talvez ter uma função que remove uma específica bloqueada sinal.

Para obter mais informações, leia a página homem .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top