2009-01-11 36 views
1

因为我目前只在C中使用this project,所以直到现在,我才使用我的web服务器作为单线程应用程序。但是,我不想再这样了!所以我有以下代码来处理我的工作。转换为多线程套接字应用程序

void BeginListen() 
{ 
     CreateSocket(); 

     BindSocket(); 

     ListenOnSocket(); 

     while (1) 
     { 
      ProcessConnections(); 
     } 
} 

现在我的ProcessConnection();开始其helpes我允许多个连接前加入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();我想杀死该进程并且只运行父线程。

+0

这个问题是不是真的做线程 - 因为你用叉子,它是一个多进程的Web服务器,而不是多线程的。 – frankodwyer 2009-01-11 19:36:20

+0

@frankodwyer,我很高兴看到pthreads上的教程或例子,fork只是一个例子。 – 2009-01-11 19:37:58

回答

4

我不是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安装一个信号手柄,只需在waitpid中用-1循环,直到没有更多的子进程,比如这

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

的waitpid函数函数将返回退出,所以,如果你愿意,你可以关联到这个关于连接的其他一些信息的子进程的PID(如果你没有保持跟踪的pid)。请记住,如果发现SIGCHLD,接受可能会在errno设置为EINTR的情况下退出,而没有有效的连接,所以请记住检查接受的返回。

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

0

检查fork()的返回值。如果它是零,你就是子进程,你可以在完成你的工作后退出()。如果它是一个正数,那么它是新创建的进程的进程ID。这可以让你杀死()子进程,如果他们由于某种原因而徘徊太久。

2

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

我认为你正试图实现一个每个请求进程的模型,就像传统的unix web服务器,比如NCSA httpd或Apache 1。X,或可能建立与共享的全局存储器的多线程服务器:

过程每次请求服务器:

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

这对于unix是比较高效的,因为进程内存不是物理复制的 - 页面在进程之间共享。当子进程写入内存时,系统使用一种名为copy-on-write的机制在逐页基础上进行复制。因此,unix上每个请求进程的服务器的开销并不是很大,许多系统都使用这种体系结构。

0

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

如果你想要一个简单的方法来让它接受多个连接(并且你不关心性能太多),那么你可以使它与inetd一起工作。这留下了产生进程并作为inetd的守护进程的工作,并且您只需编写一个处理和处理单个连接的程序。 编辑:或者,如果这是一个编程练习,你可以抓住的inetd的来源,看看它是如何做它

你也可以做你想做的事没有任何线程或新工艺,利用选择什么。

这里是一个article说明如何使用select(相当低的开销相比,叉或线程 - 这里是这样写一个轻量级的Web服务器的example

此外,如果你不执着于做这C和C++都可以,您可以考虑移植您的代码以使用ACE。这也是寻找设计模式的好地方,因为我相信它支持几乎所有的连接处理模型,并且非常便携。

1
更好

使用select()函数,使u到倾听来自不同 请求在一个程序连接....它避免阻塞而分叉为程序的副本从而导致内存中创建一个新的地址空间 低效....

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

即U可以

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 
}