首页 > 代码库 > 简单的listen+fork accept服务器实现

简单的listen+fork accept服务器实现

前段时间在分析nginx的IO模型的时候,知道nginx采用的是一进程监听+多进程accept的模式,也就是主进程先创建socket并bind指定端口listen之后,再fork出若干个子进程,由子进程去accept连接请求。

这段时间将其用代码实现并验证了一遍,监听端口后对接受到的请求,按照http协议返回进程id,算是一个极其简单的web服务器吧,后续对http协议的内容了解深入之后,看看再怎么对其扩展下。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

int s;//socket
typedef enum//定义bool类型,c语言中没有bool类型
{
    false,
    true
}bool;

bool init (const char* addr, const short port)
{
    struct sockaddr_in servaddr;
    
    s = socket(AF_INET, SOCK_STREAM, 0); 
    if(-1 == s)
        return false;
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;
    if(inet_pton(AF_INET, addr, &servaddr.sin_addr) < 0)
        return false;
    servaddr.sin_port = htons(port);
    if(bind(s, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) 
    {   
        printf("port is in use\n");
        return false;
    }   
    if(listen(s, 10) == -1) 
        return false;
    
    printf("init success! listen port %d\n", port);
    return true;
}

void do_cycle()
{
    int connect_fd;
    char rcvbuf[102400];
    char sndbuf[102400];
    pid_t pid = getpid();

    while(true)
    {
        if((connect_fd = accept(s, (struct sockaddr*)NULL, NULL)) == -1)
        {
            printf("accept socket error\n");
            continue;
        }

        recv(connect_fd, rcvbuf, 102400, 0);
        snprintf(sndbuf, 102400, "HTTP/1.1 200 OK\r\n\r\n<html><head><title>hello</title></head><body>process id: %d</body></html>", pid);
        send(connect_fd, sndbuf, strlen(sndbuf), 0);
        close(connect_fd);
    }
}

void loop_cycle(int process_num)
{
    int i, j;
    pid_t pid;
    pid_t sub_ids[process_num];
    char killstr[1024];
    char ch;
    if(1 == process_num)
        do_cycle();
    else
    for (i = 0; i < process_num; i++)
    {
        pid = fork();
        if(pid < 0)
        {
            printf("fork error\n");
            for(j = 0; j < i; j++)
            {
                snprintf(killstr, 1024, "kill -9 %d", sub_ids[j]);
                system(killstr);
            }

            return;
        }
        if(pid == 0)
            do_cycle();
        if(pid > 0)
            sub_ids[i] = pid;
    }
    while(true)
    {
        scanf("%c", &ch);
        if(ch == q)
        {
            for(j = 0; j < process_num; j++)
            {
                snprintf(killstr, 1024, "kill -9 %d", sub_ids[j]);
                system(killstr);
            }
            close(s);
            break;
        }
    }
}


int main(int argc, char* args[])
{
    int port, pro_num;
    if(argc < 4)
    {
        printf("usage: hs <ip> <port> <process num>\n");
        return 0;
    }
    port = atoi(args[2]);
    pro_num = atoi(args[3]);
    if(!init(args[1], port))
        return 0;

    loop_cycle(pro_num);
    return 0;
}

在init函数中,创建socket并进行监听,然后在loop_cycle中fork子进程,由子进程进行循环接收处理数据,父进程等待用户输入,如果用户输入退出指令,结束所有子进程并退出。
运行程序:

技术分享


启动后,用浏览器访问效果:

技术分享

技术分享

技术分享

返回了进程id.

 

简单的listen+fork accept服务器实现