• 如何写一个简单的webserver(一):最简实现


    本文主要讲述如何用C/C++在Linux环境下写一个简单的支持并发的web服务器,并不考虑服务器的健壮性、安全性、性能等一系列因素。 
    在本文中,该服务器仅支持GET请求。 

    项目地址:https://github.com/imndszy/webserver 
    开发环境:ubuntu 16.04,

    在编写一个服务器之前,我们需要对socket以及网络协议尤其是http协议有基础的了解,如果不了解,请参阅Beej’s Guide to Network ProgrammingUNIX网络编程卷一

    当我们打开一个页面时,浏览器会向相应URL所绑定的IP地址发送请求,然后远端的webserver就解析并处理这个请求,比如说如果是GET请求,就会返回一个页面或其他数据。 
    那么一个webserver的工作流程是怎样的呢?在此我们只讨论一个最简单的服务器且对单个进程而言。 
    这里写图片描述 
    上图中,socket()用于创建一个套接字,该套接字用于监听某个端口,在bind()中套接字与端口绑定,当然还有一些其他参数,随后通过listen()进行监听,这时候就进入服务器程序的主循环,当有连接建立后,accpet()被调用并返回一个新的套接字用于处理连接,这时派生一个子进程进行处理,子进程中recv()从缓冲区读取数据交由相关函数处理,处理完毕后的结果通过send()发送出去,随后关闭用于处理的套接字,子进程退出。

    至此,一个简易webserver的雏形就显现出来了。

    首先是如何创建及设置套接字的代码:

    int sockfd;
    char *port;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    
    //从命令行参数读取要绑定到的端口号
    if(argc != 2){
        fprintf(stderr,"usage:%s <port>
    ",argv[0]);
        exit(1);
    }
    port = argv[1];
    
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    
    if((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0){
        fprintf(stderr, "getaddrinfo:%s
    ", gai_strerror(rv));
        return 1;
    }
    
    for(p = servinfo;p != NULL;p = p->ai_next){
        if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
            perror("server: socket");
            continue;
        }
        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
            perror("setsockopt");
            exit(1);
        }
        if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
            close(sockfd);
            perror("server:bind");
            continue;
        }
    
        break;
    }
    

    接下来是主循环部分:

    while(1){
        sin_size = sizeof(other_addr);
        connfd = accept(sockfd, (struct sockaddr *)&other_addr, &sin_size);
        if(connfd == -1){
            perror("accept");
            continue;
        }
    
        inet_ntop(other_addr.ss_family, get_in_addr((struct sockaddr*)&other_addr), s, sizeof(s));
        printf("server:got connection from %s
    ",s);
    
        if(!fork()){
            close(sockfd);                       //fork以后子进程中也会有一个sockfd
            if(recv(connfd, buf, MAXBUF, 0) == -1) {
                perror("receive");
                close(connfd);
                exit(1);
            }
    
            sscanf(buf,"%s %s %s",method,uri,version);
    
            if(!strcasecmp(method, "GET"))     //如果是GET请求
                process_http_get(connfd,uri,filename);
            else
                client_error(connfd,method,"501","Not Implemented","Webserver does not implement this method");
    
            close(connfd);
            exit(0);
        }
        close(connfd);
    }
  • 相关阅读:
    python的性能了解
    工作记录01/17/11
    继承或者重写django的user model?
    dunder=double underscore
    ipython应该是个好的命令行式的python ide,不过现在没时间折腾。
    django的settings如何在不同环境下进行切换
    pythonic实践
    关于递归函数的简单认识
    数据结构(C语言版)链表相关操作算法的代码实现
    数据结构(C语言版)顺序栈相关算法的代码实现
  • 原文地址:https://www.cnblogs.com/ct20150811/p/9488048.html
Copyright © 2020-2023  润新知