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

Posted 重复啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单的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服务器实现的主要内容,如果未能解决你的问题,请参考以下文章

调用 fork() 后 SSL_accept 挂起

从listen and fork 到xinetd

为啥我的 Winsock 应用程序有时在 listen() 处等待,有时在 accept() 处等待?

linux 多进程并发服务__关于子进程回收的方法

Nginx是如何处理一个请求

linux下socket编程