【问题标题】:Unix networking programming in CC中的Unix网络编程
【发布时间】:2021-11-09 10:08:16
【问题描述】:
/*
** server.c -- a stream socket server demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "3490"  // the port users will be connecting to

#define BACKLOG 10   // how many pending connections queue will hold

void sigchld_handler(int s)
{
    (void)s; // quiet unused variable warning

    // waitpid() might overwrite errno, so we save and restore it:
    int saved_errno = errno;

    while(waitpid(-1, NULL, WNOHANG) > 0);

    errno = saved_errno;
}


// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        exit(1);
    }

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener
            if (send(new_fd, "www.luv2shop.net", 16, 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

    return 0;
}

用c学习网络编程

不明白这段代码在做什么以及我们为什么需要它。 我正在关注 Beej 的网络编程指南。

sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

来自 server.c 的代码

还有 void sigchld_handler(int s) 在做什么以及我们在哪里使用它?

【问题讨论】:

  • what is that void sigchld_handler(int s) “在做什么”是什么意思? where are we using it? 你是什么意思?它在您提供的来源中使用,您的“哪里”是什么意思?
  • sigchld_handler 是一个函数,它设置为程序接收到 SIGCHLD 信号时的动作(表示子进程已停止或终止)。它等着孩子。 (我认为这对于服务器程序是必要的,以防止在没有其他程序等待它们时发生“僵尸”进程)。
  • if (bind(sockfd, p-&gt;ai_addr, p-&gt;ai_addrlen) == -1) { close(sockfd); perror("server: bind"); continue; close()。
  • @wildplasser 应该省略的是continue,或者替换为return。必须关闭套接字。
  • 实际上他在之前的失败setsockopt()之后错过了关闭。 @wildplasser

标签: c sockets


【解决方案1】:

这段代码在做什么

它正在注册一个信号处理程序以在接收到SIGCHLD 信号时执行。 https://en.wikipedia.org/wiki/Signal_(IPC)https://man7.org/linux/man-pages/man7/signal.7.html

我们在哪里使用它?

信号处理程序调用waitpid() 来移除僵尸进程。代码在子进程终止时执行:父进程接收到SIGCHLD信号并在信号处理程序中执行wait*()调用,通知内核以便内核可以删除子进程。 https://en.wikipedia.org/wiki/Process_statehttps://en.wikipedia.org/wiki/Zombie_process

那个 void sigchld_handler(int s) 在做什么

它声明了一个名为sigchld_handler() 的函数,它不返回任何内容并接受一个int 类型的参数。 https://en.cppreference.com/w/cpp/language/function

【讨论】:

    【解决方案2】:

    程序派生子进程。父进程有责任为他们wait。这会清理子进程表中的条目。如果你不这样做,你的孩子一旦离开就会变成僵尸。

    此代码为 SIGCHLD 信号设置一个处理程序。如果子进程结束,该信号将发送到进程。然后,处理程序使用waitpid 获取所有已退出子项的退出状态。

    请注意,此程序不使用子项的退出代码。

    【讨论】:

    • 如果你的程序不是长时间运行的,我认为没有必要等待子进程(因为 init 进程会在父进程死后继承孤儿,并等待它们)。但是像这个例子中的“服务器”程序应该基本上无限期地运行,所以僵尸会逗留同样长的时间;最好将它们从进程列表中删除。
    • @Peter-ReinstateMonica 那么这是一个鲍勃吗?
    • @user207421 什么是鲍勃?
    • @user 啊。 “更好地删除它们”的承诺还不够吗?
    • 只有服务器进程才需要。其他进程可以愉快地忽略这个问题。我写评论的原因是为了限定 koder 的“等待它们是父进程的责任”并说“并非总是如此:除非您正在编写服务器,否则您可以将其留给 init。”
    猜你喜欢
    • 1970-01-01
    • 2015-09-12
    • 2018-02-28
    • 1970-01-01
    • 2011-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多