当突然退出C程序中循环时,为什么会发生其他循环迭代



考虑下面的基本客户端和服务器程序(只需裸露骨头/即可说明我的问题(。客户端启动与服务器的连接,提示用户输入消息,然后将其发送到服务器并打印到屏幕。

如果我在循环中间突然退出客户端程序(例如,通过关闭终端窗口(,有时客户端将继续在循环中迭代一段时间(即,最后发送到服务器/发送到服务器/当前驻留在客户关闭时的写缓冲区中,通常将其发送到服务器,通常直到循环耗尽为止(。但是,其他时间,读取服务器上的read((调用正确返回0,并且连接无问题(行为似乎是相当随机的(。

我不太了解这里发生了什么。首先,为什么程序关闭后会发生其他循环迭代?何时关闭终端窗口和实际过程本身何时结束时,是否只有一个滞后时间?即使确实发生了额外的循环迭代,也不应该在用户输入消息之前呼叫FGETS((块?

我正在使用XFCE桌面的Fedora 25工作站。

我尝试搜索有关此信息的信息,但没有太多运气(我不确定如何以简洁的方式搜索此信息(。非常感谢任何帮助。

谢谢

客户端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(3000);
    inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
    connect(sockfd, (struct sockaddr *)&server, sizeof(server));
    int i;
    for (i = 0; i < 20; i++) {
        char buf[512];
        printf("Send a message: ");
        fgets(buf, 512, stdin);
        write(sockfd, buf, sizeof(buf));
    }
    close(sockfd);
}

服务器:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(3000);
    inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
    bind(listenfd, (struct sockaddr *)&server, sizeof(server));
    listen(listenfd, 10);
    printf("Listening...n");
    struct sockaddr_in client;
    socklen_t client_size = sizeof(client);
    int clientfd = accept(listenfd, (struct sockaddr *)&client, &client_size);
    for (;;) {
        char buf[512];
        int i = read(clientfd, buf, sizeof(buf));
        if (i == 0) {
            close(clientfd);
            printf("Connection Closedn");
            break;
        } else {
            printf("%s", buf);
        }
    }
    close(listenfd);
}

当您的终端(以及连接到您的过程的stdin/out/err(的PTY设备的远程/主侧已关闭时,fgets将在stdin上看到文件终止状态,并且将立即使用不完整的行(不在n中结束(或根本没有输入(null返回值(返回;实际上,这将是后者。如果您检查了结果,则可以看到并可以采取相应的行动。

在服务器中,此行:

printf("%s", buf); 

应替换为:

*buf[i] = 'n'; 
printf( "%s", buf );

因此,有一个有效的字符串打印(read()不会终止字符串(

注意:如果发生I/O错误或信号发生,等等,则read()将返回一个小于0的值,应导致退出for(;;(循环,而不是在循环中继续,打印''buf[]的旧内容

在客户端的这一行中:

write(sockfd, buf, sizeof(buf));

如果写入失败,则如果发生此类事件时,它将返回小于0的值,则循环应退出,不继续循环,

检查所有错误条件非常重要。进行错误检查(以及由此导致的错误处理(可以轻松地将代码的大小增加一倍,但必须完成;否则,您看到的这样的"奇数"事件将发生,而没有简单地解释发生的事情。

当系统函数返回错误指示时,请使用函数perror()stderr上提供了一些文本,以及系统中的消息,即为什么会认为发生错误。

如果我突然退出循环中间的客户端程序(例如,通过关闭终端窗口(,

关闭终端窗口不会退出客户端程序 - 它会继续运行,只是没有输入(因此,从现在关闭的终端中的任何读取都将返回EOF(。但是,您永远不会检查客户端中fgets的返回值,因此您从未注意到,只会继续循环,发送相同的缓冲区。

另外,代码:

fgets(buf, 512, stdin);
write(sockfd, buf, sizeof(buf));

从输入中读取最多511个字符的线,但随后发送整个512字节缓冲区,无论实际消息多长时间。您想要的更像是:

if (fgets(buf, 512, stdin))
    write(sockfd, buf, strlen(buf));
else
    break;  // there was an error or EOF on reading from stdin

当然,这仍然存在超过511个字节的行问题,然后有一个问题,即TCP不会保留消息边界,因此在服务器上,您可能会在单个读取呼叫中获得多个或仅一部分消息的一部分。

相关内容

  • 没有找到相关文章

最新更新