Izzo
Izzo

Reputation: 4928

Using the POSIX "write" function after killing my TCP/IP connection crashes my application - why?

I'm working on a C application that uses POSIX TCP/IP functions for communicating with a server. I'm currently doing some testing to see how the application responds when the connection unexpectedly closes.

The main workhouse function is shown below:

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;
}

This function works as expected when I have a good connection with the server. However, calling this function after killing the connection crashes my application.

Ideally, I would want the write function to return an error indicating that the write failed. This would then allow me to handle the error and transition my program to the appropriate state. However, this is not what happens.

I'm curious as to why this function call would crash the application. I'm somewhat thinking that it may be a problem where the function call doesn't lock, and then the pointer its referencing becomes 'bad' resulting in a segmentation fault.

Here is how I configured my 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;        
}

Any help would be appreciated

Upvotes: 0

Views: 238

Answers (1)

user207421
user207421

Reputation: 310869

Further to the missing error-checking @RemyLebeau mentioned, you are also not error-checking the write() itself:

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

Here you are ignoring the possibilty that it returned -1, in which case you should call perror() or construct an error message string with strerror() and print it, and close the socket, and tell the caller so he doesn't keep writing.

You also need to set SIGPIPE to SIG_IGNORE or whatever it is, so that EPIPE write errors don't cause SIGPIPE signals.

And all this ERR_NET_COULD_NOT_GET_SOCKET_OPTION stuff is poor practice. You should return the actual errno value, or at least print it, not just in the getsockopt() case but in all error cases.

And you are doing the connect() in blocking mode. The following select() is therefore completely pointless.

Upvotes: 1

Related Questions