应用层协议实现系列(一)——HTTP服务器之仿nginx多进程和多路IO的实现

最近在尝试自己写一个Http服务器,在粗略研究了nginx的代码之后,决定仿照nginx中的部分设计自己实现一个高并发的HTTP服务器,在这里分享给大家。

目前使用的较多的Http服务器就是apache和nginx,apache的主要特点就是稳定,而nginx的主要特点是承载的并发量高。在这里从实现原理上做一个分析:

apache采用的是多进程服务器模型,即服务器每监听到一个连接时,会创建一个新的进程去处理连接,进程与进程之间是独立的,因此就算进程在处理连接的过程中崩溃了,也不会影响其他进程的运行。但由于服务器能创建的进程数目与内存有关,因此服务器的最大并发数会受到机器内存的影响,同时如果有人发起大量恶意长链接攻击,就会导致服务器超载。

nginx采用的是多路IO复用服务器模型,即服务器每监听到一个连接时,会将连接加入到连接数组中,使用epoll多路IO复用函数对每个连接进行监听,当连接中有数据到来时,epoll会返回相应的连接,依此对各个连接进行处理即可。epoll的最大连接数量虽然也会受到内存的影响,但由于每个未激活的连接占用的内存很小,所以相比于apache可以承载更大的并发。

但由于多路IO复用是在一个进程中进行的,所以如果在处理连接的过程中崩溃了,其他连接也会受到影响。为了解决这个问题,nginx中也引入了多进程,即nginx服务器由一个master进程和多个worker进程组成。master进程作为父进程在启动的时候会创建socket套接字以及若干个worker进程,每个worker进程会使用epoll对master创建的套接字进行监听。当有新连接到来时,若干个worker进程的epoll函数都会返回,但只有一个worker进程可以accept成功。该进程accept成功之后将该连接加入到epoll的监听数组中,该连接之后的数据都将由该worker进程处理。如果其中一个worker进程在处理连接的过程中崩溃了,父进程会收到信号并重启该进程以保证服务器的稳定性。

另外,每次新连接到来都会唤醒若干个worker进程同时进行accept,但只有一个worker能accept成功,为了避免这个问题,nginx引入了互斥信号量,即每个worker进程在accept之前都需要先获取锁,如果获取不到则放弃accept。

在明确了上述原理之后,我们就可以仿照nginx实现一个http服务器了。首先是创建套接字的函数:

//创建socket
int startup(int port) {
    struct sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));
    //协议域(ip地址和端口)
    servAddr.sin_family = AF_INET;
    //绑定默认网卡
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //端口
    servAddr.sin_port = htons(port);
    int listenFd;
    //创建套接字
    if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }
    unsigned value = 1;
    setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
    //绑定套接字
    if (bind(listenFd, (struct sockaddr *)&servAddr, sizeof(servAddr))) {
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }
    //开始监听,设置最大连接请求
    if (listen(listenFd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }
    return listenFd;
}

该函数创建了一个套接字并将其绑定到了一个端口上开始监听。由于我们接下来要创建若干个worker进程,可以通过fork函数实现:

//管理子进程的数组,数组多大就有几个子进程
static int processArr[PROCESS_NUM];
//创建若干个子进程,返回当前进程是否父进程
bool createSubProcess() {
    for (int i=0; i<GET_ARRAY_LEN(pr
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值