c - TCP 无法检测到客户端中已关闭的套接字

标签 c tcp

我的服务器刚连接到客户端,然后断开连接,同时客户端尝试将整数发送到已关闭的套接字(scanf 是为了确保服务器首先关闭它)。我使用带有 MSG_NOSIGNAL 的发送并检查 EPIPE,但未设置标志。我认为结果应该打印值为 -1 或 0,但它等于 1,因为我在已经关闭的套接字上写入。谁能解释一下?

服务器代码:

#define QUEUE_LENGTH     5
#define PORT_NUM     10002
#define BUFFER_SIZE 512000

int main(int argc, char *argv[]) {
    int sock, msg_sock;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    socklen_t client_address_len;

    sock = socket(PF_INET, SOCK_STREAM, 0); // creating IPv4 TCP socket
    if (sock < 0)
        syserr("socket");

    server_address.sin_family = AF_INET; // IPv4
    server_address.sin_addr.s_addr = htonl(
            INADDR_ANY); // listening on all interfaces
    server_address.sin_port = htons(PORT_NUM); 

    // bind the socket to a concrete address
    if (bind(sock, (struct sockaddr *) &server_address,
             sizeof(server_address)) < 0)
        syserr("bind");

    // switch to listening (passive open)
    if (listen(sock, QUEUE_LENGTH) < 0)
        syserr("listen");

    printf("accepting client connections on port %hu\n",
           ntohs(server_address.sin_port));


    for (;;) {
        client_address_len = sizeof(client_address);
        msg_sock = accept(sock, (struct sockaddr *) &client_address,
                          &client_address_len);
        if (msg_sock < 0)
            syserr("accept");



        printf("ending connection\n");
        if (close(msg_sock) < 0) {
            printf("ErrorClosingSocket\n");
            break;
        }
        continue;
    }
    return 0;
}

客户端代码:

int sendSomething(void *to_send, int socket, uint32_t length) {

    if (send(socket, to_send, length, MSG_NOSIGNAL) !=
        length) {
        if (errno == EPIPE)  // Sending on closed connection
            return 0;
        return -1;
    }

    return 1;
}

int main(int argc, char *argv[]) {
    int sock;
    struct addrinfo addr_hints;
    struct addrinfo *addr_result;

    int err;

    if (argc != 3)
        fatal("Usage: %s host port\n", argv[0]);

    // 'converting' host/port in string to struct addrinfo
    memset(&addr_hints, 0, sizeof(struct addrinfo));
    addr_hints.ai_family = AF_INET; // IPv4
    addr_hints.ai_socktype = SOCK_STREAM;
    addr_hints.ai_protocol = IPPROTO_TCP;
    // argv[1] is localhost and argv[2] is 10002 
    err = getaddrinfo(argv[1], argv[2], &addr_hints, &addr_result);
    if (err == EAI_SYSTEM) // system error
        syserr("getaddrinfo: %s", gai_strerror(err));
    else if (err != 0) // other error (host not found, etc.)
        fatal("getaddrinfo: %s", gai_strerror(err));


    // initialize socket according to getaddrinfo results
    sock = socket(addr_result->ai_family, addr_result->ai_socktype,
                  addr_result->ai_protocol);
    if (sock < 0)
        syserr("socket");

    // connect socket to the server
    if (connect(sock, addr_result->ai_addr, addr_result->ai_addrlen) < 0)
        syserr("connect");

    freeaddrinfo(addr_result);


    int result;
    scanf("%d", &result);
    uint16_t test;

    test = htons(1);
    result = sendSomething(&test, sock, sizeof(test));
    printf("result:%d\n", result);

    if (close(sock) < 0) {
        printf("ErrorClosingSocket\n");
    }

    return 0;
}

注意:Fatal和Syserr只是为了报错

最佳答案

这就是 TCP 的工作方式。当服务器关闭套接字时,会向客户端发送一个 FIN。这表示服务器将不再发送任何数据。这并不一定意味着它不想接收更多数据。

因此,客户端可以在套接字上调用 send() 而操作系统不会报告错误。如果服务器确实关闭了整个套接字,那么它将发送一个TCP 重置数据包 作为对指示该情况的传入数据的响应。现在,套接字上的 future 操作(写/关闭)将指示错误。

服务器(或任何对等方)确实有可能仅使用系统调用 shutdown() 中途关闭连接(读取或写入端)。如果服务器关闭写入连接,网络上会发生同样的事情,就好像服务器用 close() 关闭了整个连接一样。更高级别的协议(protocol)有责任确定何时应为每一方关闭连接。

如果你想确保你发送的所有数据确实被对等方确认,你可以使用 SO_LINGER 套接字选项。但更常见的方式是,将此确保作为通信协议(protocol)的一部分,即一部分请求关闭更高级别的连接(例如,smtp QUIT 命令)和对等方通过关闭 tcp 连接对其作出 react 。

关于c - TCP 无法检测到客户端中已关闭的套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55676029/

相关文章:

tcp - Graylog 收集器连接被拒绝

linux - TCP 缓冲速率消耗

c# - 如何正确处理 "existing connection forcibly closed"

c++ - cpp socket - TCP 传输(使用相同的端口发送和接收)

c# - 数据已经发送后服务器不发送数据

c# - 键盘操作

c - 链表的基本问题

c - 在C中读取整数时对字符进行错误检查

c - 将 NaN 定义为 0

c - C 中的退出过程值