【问题标题】:Converting to Multi-Threaded Socket Application转换为多线程套接字应用程序
【发布时间】:2010-09-30 19:30:59
【问题描述】:

由于我目前只在 C 语言中执行 this project,因此到目前为止我只将我的网络服务器用作单线程应用程序。不过,我不想再这样了!所以我有以下代码来处理我的工作。

void BeginListen()
{
        CreateSocket();

        BindSocket();

        ListenOnSocket();

        while ( 1 )
        {
            ProcessConnections();
        }
}

现在我在ProcessConnection(); 开始之前添加了fork();,这有助于我允许多个连接!但是,当我添加代码以守护在this answer 中找到的应用程序时。我遇到了一个小问题,使用fork() 将创建我整个正在运行的应用程序的副本,这就是fork() 的目的。所以,我想解决这个问题。

我的ProcessConnection() 看起来像这样

void ProcessConnections()
{
        fork();

        addr_size = sizeof(connector);

        connecting_socket = accept(current_socket, (struct sockaddr *)&connector, &addr_size);

        if ( connecting_socket < 0 )
        {
                perror("Accepting sockets");
                exit(-1);
        }

        HandleCurrentConnection(connecting_socket);


        DisposeCurrentConnection();
}

我将如何简单地在connecting=socket = accept... 上方或之后添加几行以使其同时接受多个连接?我可以使用fork(); 但是当它归结为DisposeCurrentConnection(); 时,我想终止该进程并让父线程运行。

【问题讨论】:

  • 这个问题与线程无关——因为您使用的是 fork,它是一个多进程网络服务器,而不是多线程。
  • @frankodwyer,我也很高兴看到有关 pthreads 的教程或示例,fork 只是一个示例。

标签: c multithreading posix unix


【解决方案1】:

检查 fork() 的返回值。如果为零,则您是子进程,您可以在完成工作后退出()。如果它是一个正数,那么它是新创建的进程的进程 ID。这可以让您 kill() 子进程,如果它们由于某种原因而停留太久。

【讨论】:

    【解决方案2】:

    我不是 100% 确定你想要做什么,我想买下我的头,我更愿意在接受后做分叉,当你'重做。但请记住,当子进程退出时,您需要对 SIGCHLD 信号做出反应,否则您将有大量的僵尸进程徘徊,等待将它们的退出状态传递给父进程。 C-伪代码:

    for (;;) {
      connecting_socket = accept(server_socket);
      if (connecting_socket < 0)
        {
          if (errno == EINTR)
            continue;
          else
            {
              // handle error
              break;
            }
        }
    
      if (! (child_pid = fork ()))
        {
           // child process, do work with connecting socket
           exit (0);
        }
      else if (child_pid > 0)
        {
          // parent process, keep track of child_pid if necessary.
        }
      else
        {
          // fork failed, unable to service request, send 503 or equivalent.
        }
    }
    

    需要 child_pid(如前所述)来杀死子进程,但如果您希望使用 waitpid 来收集退出状态。

    关于僵尸进程,如果您对进程发生的事情不感兴趣,您可以为 SIGCHLD 安装一个信号处理程序,然后使用 -1 在 waitpid 上循环,直到不再有子进程,就像这样

    while (-1 != waitpid (-1, NULL, WNOHANG))
      /* no loop body */ ;
    

    waitpid 函数将返回退出的子进程的 pid,因此,如果您希望将其与有关连接的其他一些信息相关联(如果您确实跟踪了 pid)。请记住,如果捕获到 SIGCHLD,accept 可能会在 errno 设置为 EINTR 的情况下退出,而没有有效的连接,因此请记住在接受返回时检查这一点。

    编辑:
    不要忘记检查错误条件,即 fork 返回 -1。

    【讨论】:

    • 您能否提供一些关于如何处理僵尸进程的示例?
    • 在我完成 kill(child_pid,9) 之后可以添加 while 循环吗?
    • 开始杀死子进程没有什么意义,对信号做出反应会更加健壮,因为无论您是杀死它还是它自己退出都会调用它。如果你真的想要在 kill 之后使用它,请改用 wait(pid),它会等待进程终止。
    【解决方案3】:

    在 unix 上谈论 fork() 和线程并不完全正确。 Fork 创建了一个全新的进程,它与父进程没有共享地址空间。

    我认为您正在尝试实现按请求处理的模型,很像传统的 unix Web 服务器,例如 NCSA httpd 或 Apache 1.x,或者可能构建具有共享全局内存的多线程服务器:

    按请求处理的服务器:

    当您调用 fork() 时,系统会创建父进程的克隆,包括文件描述符。这意味着您可以接受套接字请求然后分叉。子进程有套接字请求,它可以回复然后终止。

    这在 unix 上是相对有效的,因为进程的内存不是物理复制的——页面是在进程之间共享的。当子进程写入内存时,系统使用一种称为写时复制的机制来逐页复制。因此,unix 上的每请求进程服务器的开销并不大,许多系统都使用这种架构。

    【讨论】:

      【解决方案4】:

      根据我的评论,这个服务器并不是真正的多线程,它是多进程的。

      如果您想要一种简单的方法使其接受多个连接(并且您不太关心性能),那么您可以使用inetd 使其工作。这将产生进程和作为守护进程的工作留给inetd,您只需要编写一个程序来处理和处理单个连接。 编辑:或者如果这对你来说是一个编程练习,你可以获取 inetd 的源代码,看看它是如何工作的

      您也可以在没有线程或新进程的情况下使用 select 执行您想做的事情。

      这是一个 article,它解释了如何使用 select(与 fork 或线程相比开销相当低 - 这是一个以这种方式编写的轻量级 Web 服务器的 example

      此外,如果您不喜欢在 C 中执行此操作,并且 C++ 也可以,您可以考虑移植您的代码以使用 ACE。这也是寻找如何做到这一点的设计模式的好地方,因为我相信它几乎支持任何连接处理模型并且非常便携。

      【讨论】:

        【解决方案5】:

        最好使用 select() 函数,它使你能够从不同的地方收听和连接 一个程序中的请求......它避免了阻塞,但分叉创建了一个新的地址空间 对于导致内存效率低下的程序副本....

        select(Max_descr, read_set, write_set, exception_set, time_out);
        

        你可以

        fd_set* time_out;
        fd_set* read_set;
        listen(1);
        listen(2);
        while(1)
        {
          if(select(20, read_set, NULL,NULL, timeout) >0)
          {
            accept(1);
            accept(2); .....
            pthread_create(func);....
          }
          else
        }
        

        【讨论】:

          猜你喜欢
          • 2017-12-12
          • 1970-01-01
          • 2011-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-11
          相关资源
          最近更新 更多