C echo server 终止后仍然回显一次

C echo server still echoes once after being terminated

我用 C 写了一个简单的 echo 服务器和一个客户端。

这是服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include "unp.h"

#define SERVER_PORT 10000

void start_echo_service(int connfd);
void SIGCHLD_handler(int signum);

int main()
{
    int listenfd, connfd;
    socklen_t len;
    struct sockaddr_in server_addr, client_addr;

    pid_t child_pid;

    printf("***Starting the echo server***\n");

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Failed to create connection socket. Exiting...\n");
        exit(0);
    }

    bzero(&server_addr, sizeof(struct sockaddr_in));
    bzero(&client_addr, sizeof(struct sockaddr_in));

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    if(bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
    {
        strerror(errno);
        exit(0);
    }

    if(listen(listenfd, 1000) < 0)
    {
        strerror(errno);
        exit(0);
    }

    /* Add the handler for SIGCHLD */
    struct sigaction sigchld_action;
    sigchld_action.sa_handler = SIGCHLD_handler;
    sigemptyset(&sigchld_action.sa_mask);
    sigchld_action.sa_flags = 0;
    if(sigaction(SIGCHLD, &sigchld_action, NULL) < 0)
    {
        printf("Error while adding handler for SIGCHLD\n");
        exit(0);
    }

    while(1)
    {
        len = sizeof(client_addr);
        if((connfd = accept(listenfd, (struct sockaddr *) &client_addr, &len)) < 0)
        {
            if(errno == EINTR)
                continue;
            else
            {
                strerror(errno);
                exit(0);
            }
        }

        child_pid = fork();
        if(child_pid < 0)
        {
            //some error occured
            strerror(errno);
            exit(0);
        }
        else if(child_pid == 0)
        {
            // child process
            close(listenfd);
            start_echo_service(connfd);
            close(connfd);
            exit(1);
        }

        close(connfd);
    }
    close(connfd);

    return 1;
}

void SIGCHLD_handler(int signum)
{
    pid_t pid;
    if((pid = waitpid(-1, NULL, 0)) > 0)
        printf("Harvested child (pid): %d\n", pid);
}

void start_echo_service(int connfd)
{
    char buf[256];
    int bytes_read;

    while(1)
    {
        if((bytes_read = read(connfd, buf, sizeof(buf))) > 0)
        {
            writen(connfd, buf, bytes_read);
            continue;
        }
        else if(bytes_read == 0)
            break;
        else
        {
            if(errno == EINTR)
                continue;
            else
            {
                printf("Read error\n");
                break;
            }
        }
    }
}

客户端代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "unp.h"

#define SERVER_PORT 10000

void start_echo_client(int connfd);

int main(int argc, char *argv[])
{
    int connfd;
    struct sockaddr_in server_addr;

    if(argc != 2)
    {
        printf("Usage: echo_client <server-address>\n");
        exit(-1);
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET, argv[1], &(server_addr.sin_addr));
    server_addr.sin_port = htons(SERVER_PORT);

    if((connfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        strerror(errno);
        exit(0);
    }

    if(connect(connfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
    {
        strerror(errno);
        exit(0);
    }

    start_echo_client(connfd);

    return 1;
}

void start_echo_client(int connfd)
{
    char buffer[256];

    while(fgets(buffer, sizeof(buffer), stdin) != NULL)
    {
        writen(connfd, buffer, strlen(buffer));
        readn(connfd, buffer, strlen(buffer));
        printf("%s", buffer);
    }
}

我在 shell 上用 ./echo_server 启动我的服务器。 我用 ./echo_client 127.0.0.1 在另一个 shell 上启动我的客户端。到目前为止,还不错。

在我的客户端上,我输入一条消息,比如 hello。服务器回应它。现在,我用 Ctrl+C 终止我的服务器。客户端还是运行。现在,我在客户端输入另一条消息,比如 zzz。我仍然收到回声,zzz 打印在 shell 上。在客户端上键入另一条消息时,它会终止。

我想这可能与我终止它时服务器处于不间断睡眠状态有关,但我不能确定。

这里是link到unp.h and unp.c

start_echo_client 打印 buffer 而不管 readn 中的 return,不是吗?即使服务器关闭,缓冲区也会包含 writen 发生的内容(您使用相同的缓冲区进行写入和读取)。因此我认为,您假设有来自服务器的回声,但实际上没有。

使用 tcpdump 的测试也表明 FIN 确实通过服务器终端上的 ctrl-c 到达。客户端尝试 write 忽略收到 RST 的对等闭包。

另外,read FIN 上的收益不是零。我改成recv然后闭包被抓了

Return 在 readn 函数中读取的字节数,而不是当前正在执行的操作,如果它为零,则不要打印缓冲区。