C echo服务器在终止后仍然会回响一次

时间:2015-04-17 09:34:48

标签: c tcp echo server

我在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启动我的服务器。 我在另一个shell上用./echo_client 127.0.0.1启动我的客户端。到目前为止,非常好。

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

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

以下是unp.hunp.c

的链接

1 个答案:

答案 0 :(得分:2)

start_echo_client打印buffer,无论readn的回复是什么,都不是吗?即使服务器关闭,缓冲区也会包含writen所发生的内容(使用相同的缓冲区进行写入和读取)。因此,我认为,你假设服务器有一个回声,而不是。

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

此外,read在FIN上没有产生零。我改为recv然后关闭了。

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