在终止我的 TCP/IP 连接后使用 POSIX "write" 函数会使我的应用程序崩溃 - 为什么?

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

我正在开发一个使用 POSIX TCP/IP 函数与服务器通信的 C 应用程序。我目前正在做一些测试,看看应用程序在连接意外关闭时如何响应。

济贫院主要功能如下图:

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

当我与服务器连接良好时,此功能按预期工作。但是,在终止连接后调用此函数会使我的应用程序崩溃。

理想情况下,我希望写入函数 return 指示写入失败的错误。这将允许我处理错误并将我的程序转换到适当的状态。然而,事实并非如此。

我很好奇为什么这个函数调用会导致应用程序崩溃。我有点想这可能是函数调用没有锁定的问题,然后它引用的指针变成 'bad' 导致分段错误。

这是我配置套接字的方式:

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

如有任何帮助,我们将不胜感激

除了提到的缺少错误检查@RemyLebeau 之外,您也没有对 write() 本身进行错误检查:

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

在这里你忽略了它 returned -1 的可能性,在这种情况下你应该调用 perror() 或者用 strerror() 构造一个错误消息字符串并打印它,and 关闭套接字,and 告诉调用者不要继续写。

您还需要将 SIGPIPE 设置为 SIG_IGNORE 或其他任何值,以便 EPIPE 写入错误不会导致 SIGPIPE 信号。

所有这些 ERR_NET_COULD_NOT_GET_SOCKET_OPTION 都是糟糕的做法。您应该 return 实际 errno 值,或者至少打印它,不仅在 getsockopt() 情况下,而且在所有错误情况下。

并且您正在以阻塞模式执行 connect()。因此,以下select()完全没有意义。