2017-05-01 12 views
0

Je travaille sur une application C qui utilise les fonctions POSIX TCP/IP pour communiquer avec un serveur. J'effectue actuellement des tests pour voir comment l'application répond lorsque la connexion se ferme de façon inattendue.L'utilisation de la fonction POSIX "write" après avoir détruit ma connexion TCP/IP bloque mon application - pourquoi?

La fonction principale workhouse est présentée ci-dessous:

uint32_t netWriteMsg(uint8_t * pmsg, size_t msg_size) 
{ 
    if(write(m_sockfd, pmsg, msg_size) < msg_size) 
     return ERR_NET_NOT_ALL_BYTES_SENT; 

    return ERR_NONE; 
} 

Cette fonction fonctionne comme prévu lorsque j'ai une bonne connexion avec le serveur. Cependant, appeler cette fonction après avoir tué la connexion bloque mon application.

Idéalement, je voudrais que la fonction write renvoie une erreur indiquant que l'écriture a échoué. Cela me permettrait alors de gérer l'erreur et de faire passer mon programme à l'état approprié. Cependant, ce n'est pas ce qui se passe.

Je suis curieux de savoir pourquoi cet appel de fonction planterait l'application. Je pense en quelque sorte que cela peut être un problème où l'appel de la fonction ne se verrouille pas, et ensuite le pointeur de son référencement devient «mauvais», ce qui entraîne une erreur de segmentation.

Voici comment je configuré mon socket:

uint32_t netConnect() 
{ 
    /* locals */ 
    struct sockaddr_in serv_addr; 
    fd_set fdset_sock; // only 1 file descriptor (socket fd) will be placed in this set 
    fd_set fdset_empty; 
    struct timeval time = {NET_TIMEOUT_CONNECT, 0}; 
    int sock_error; 
    socklen_t optlen; 
    int error = ERR_NONE; 

    /* obtain socket file descriptor and set it to non-blocking */ 
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0); 

    memset(&serv_addr, 0, sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_port = htons(PORT_NO); 
    inet_pton(AF_INET, IP_ADDR, &(serv_addr.sin_addr.s_addr)); 


    /* attempt to connect */ 
    error = connect(m_sockfd, &serv_addr, sizeof(serv_addr)); 
    if(error) return ERR_NET_CONNECT_FAILED_IMMEDIATELY; 

    select(m_sockfd, &fdset_empty, &fdset_sock, &fdset_empty, &time); // blocks until socket is good or timeout occured 
    error = getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &sock_error, &optlen); 
    if(error) return ERR_NET_COULD_NOT_GET_SOCKET_OPTION; 

    if(sock_error) 
     return ERR_NET_CONNECT_ATTEMPT_TIMEOUT; 

    m_is_connected = 1; 

    return ERR_NONE;   
} 

Toute aide serait appréciée

+5

S'il n'y a aucun récepteur à l'appel d'écriture, votre processus devrait être tué par SIGPIPE. Réglez la disposition du signal de ce signal sur SIG_IG, et vous devriez commencer à obtenir une erreur EPIPE errno à la place du signal. – PSkocik

+1

Vous ne faites aucune vérification d'erreur sur 'socket()' ou 'select()', et votre vérification d'erreur sur 'connect()' est incorrecte. Si 'm_sockfd' est non-bloquant,' connect() 'retournera -1 et' errno' sera 'EINPROGRESS'. Vous devez vérifier cela avant de retourner 'ERR_NET_CONNECT_FAILED_IMMEDIATELY'. Et l'extraction de 'SO_ERROR' n'est valide que s'il y a un code d'erreur à récupérer, donc assurez-vous que' select() 'a réussi avant d'appeler' getsockopt() '. –

+2

@PSkocik: les autres options incluent 1) utiliser 'send()' au lieu de 'write()' de sorte que vous pouvez spécifier l'indicateur 'MSG_NOSIGNAL', et/ou 2) en utilisant' setsockopt() 'pour activer l'option' SO_NOSIGPIPE' . Sur les plates-formes qui prennent en charge ces options, c'est. –

Répondre

1

Suite à la @RemyLebeau manquante de contrôle d'erreur mentionné, vous n'êtes pas non plus de contrôle d'erreur lui-même write():

if(write(m_sockfd, pmsg, msg_size) < msg_size) 
    return ERR_NET_NOT_ALL_BYTES_SENT; 

ici vous ignorez la possibilité qu'il retourne -1, dans ce cas, vous devez appeler perror() ou construire un n chaîne de message d'erreur avec strerror() et l'imprimer, et fermer la prise, et dire à l'appelant de sorte qu'il ne continue pas d'écrire.

Vous devez également définir SIGPIPE sur SIG_IGNORE ou quoi que ce soit, de sorte que les erreurs d'écriture EPIPE ne provoquent pas de signaux SIGPIPE.

Et tout ce truc ERR_NET_COULD_NOT_GET_SOCKET_OPTION est une mauvaise pratique. Vous devez retourner la valeur errno réelle, ou au moins l'imprimer, pas seulement dans le cas getsockopt() mais dans tous les cas d'erreur.

Et vous faites le connect() en mode de blocage. Le select() suivant est donc complètement inutile.