嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

周梦康 发表于 2015-11-19 3949 次浏览 标签 : LinuxC

这两天“弃暗投明”先放下《Unix环境高级编程》,拿起了不到200页的《TCP/IP 网络变成技术基础》教科书。觉得这个档位正好适合自己目前的水平,等看完这个,再回过头去学习前者。

原来我一直口述的 web server 应该叫面向连接服务器,下面就是把书上的例子简化,对之前的循环服务器增加了多进程,使地其具备支持并发的能力。

这篇笔记先实现一个不固定进程数的并发模型。

多进程并发的面向连接 Web 服务器的实践(一)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <string.h>
   
#define SERV_PORT 8031
  
int main(void)
{
    int lfd, cfd;
    struct sockaddr_in serv_addr,clin_addr;
    socklen_t clin_len;
    pid_t pid;
    char buf[1024];
    int len;
   
    if((lfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
        perror("create socket failed");
        exit(1);
    }
 
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
     
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(SERV_PORT);
      
    if(bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        perror("bind error");
        exit(1);
    }
       
    if(listen(lfd, 128) == -1)
    {
        perror("listen error");
        exit(1);
    }
  
    clin_len = sizeof(clin_addr);
 
    signal(SIGCLD,SIG_IGN);
 
    while(1)
    {
         
        if((cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len)) == -1)
        {
            perror("accept error");
            exit(1);
        }
 
        pid = fork();
 
        if (pid > 0)
        {
            // 在父进程中关闭连接的套接字描述符,只是把 cfd 的引用数减少1,在子进程中还在使用 cfd
            close(cfd);
        }
        else if (pid == 0)
        {
            // 子进程关闭 lfd 处理任务,使其回到 TIME_WAIT 状态值
            close(lfd);
            len = read(cfd,buf,sizeof(buf));
 
            char web_result[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: 11\r\nServer: mengkang\r\n\r\nhello world";
            write(cfd,web_result,sizeof(web_result));
 
            close(cfd);
            exit(0);
        }
        else
        {
            perror("fork error");
            exit(1);
        }
 
    }
  
    close(lfd);
      
    return 0;
}

小插曲:

  1. 之前忘记了在子进程处理完任务之后退出了(少写了76行的exit(0)),这样子进程又会while循环执行accept,而lfd已经在子进程中关闭了,所以出现了服务器这边查看会有accept error: Bad file descriptor的错误信息,我wget了下,发现一次请求就一次报错,但是内容却是请求到了的情况。

  2. 在71行代码是注释掉的情况下,ab压测是无法执行的,报错apr_socket_recv: connection reset by peer (104)详细说明

这里还没有对子进程个数做限定,压测的时候,如果请求多,并发大,则会创建太多子进程而退出的情况,所以还需要对上面的代码的子进程做线程池管理。可以参考http://mengkang.net/576.html#server02

嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表