手把手教你用nginx开发自己的服务器------利用nginx开发一个helloWorld程序(一)

https://blog.csdn.net/zk3326312/article/details/80724297

能开始学习nginx的你,肯定也撸了不少代码了,相信你学习代码都是从helloWorld开始的,那么,今天我们就用nginx开发一个helloWorld,我们将要实现的功能就是当浏览器来访问你的服务器时,你的终端打印一个helloWorld。

先别急着开始撸代码,先聊一聊自己为什么想写这个专栏,其实本人也是个服务器开发菜鸟,感觉年纪稍微大了一点,反应和记忆力就下降的很厉害了,必须得写点什么帮助自己记忆一下。这个专栏将会帮助广大的nginx菜鸟们一点点深入理解nginx,我的每篇文章都不会很长,但都会有一个重点,如果你细心跟着我一直学习下去,你至少将学会如何用nginx搭建自己的web服务器,用nginx搭建自己的反向代理服务器,然后如果你足够认真,对于nginx的各种高级特性,诸如内存对齐带来的高效率,slab算法,各种池(内存池,连接池)也会有一定的了解。在讲解nginx的同时,我也会同时结合nginx中的很多关于网络的特性,讲解说明一些现有的前沿的知识。如http2.0,tls1.3等,如果你发现我的文章有任何纰漏之处,欢迎大家指正。

在正式开始上手nginx之前,我们先回忆下一个echo服务器是怎么做的:

#include <netdb.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
 
#define EHCO_PORT    8080
#define MAX_CLIENT_NUM        10
 
int main()
{
    int socketfd;
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
       
    if(socketfd == -1)
    {
        printf("errno=%d ", errno);
        exit(1);
    }
    else
    {
        printf("socket create successfully ");
    }
 
    struct sockaddr_in sa;
    bzero(&sa, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(EHCO_PORT);
    sa.sin_addr.s_addr = htons(INADDR_ANY);
    bzero(&(sa.sin_zero), 8);
 
    if(bind(socketfd, (struct sockaddr *)&sa, sizeof(sa))!= 0)
    {
        printf("bind failed ");
        printf("errno=%d ", errno);
        exit(1);
    }
    else
    {
        printf("bind successfully ");
    }
 
    //listen
    if(listen(socketfd ,MAX_CLIENT_NUM) != 0)
    {
        printf("listen error ");
        exit(1);
    }
    else
    {
        printf("listen successfully ");
    }
 
    int clientfd;
    struct sockaddr_in clientAdd;
    char buff[101];
    socklen_t len = sizeof(clientAdd);
    int closing =0;
    while( closing == 0  && (clientfd = accept(socketfd, (struct sockaddr *)&clientAdd, &len)) >0 )
    {
        int n;
        while((n = recv(clientfd,buff, 100,0 )) > 0)
        {
            printf("number of receive bytes = %d ", n);
            write(STDOUT_FILENO, buff, n);
            send(clientfd, buff, n, 0);
            buff[n] = '';
            if(strcmp(buff, "quit ") == 0)
            {
                break;
            }
            else if(strcmp(buff, "close ") == 0)
            {
                //server closing
                closing = 1;
                printf("server is closing ");
                break;
            }
        }
        close(clientfd);
    }
    close(socketfd);
    return 0;
}


任何服务器,都逃不过那几个基本的函数,socket().bind(),listen(),connect(),accept(),read(),write(),send(),recv()无论多么高级的服务器,本质上都是利用操作系统给的IO复用和各种设计模式对这几个接口做了封装。在学习服务器开发的时候个人觉得一定要把握这个本质(毕竟再复杂的逻辑也就是收到一段message,然后处理message,再发送相应的response回去嘛)。

回忆好一个echo服务器是怎么做的之后,再复习一下一个不得不说的基本知识:事件驱动机制。什么是事件驱动机制呢?字面理解就是,一个事件到来,才会触发下一个事件,一个事件触发一个事件,层层推进。文字说起来很抽象,其实本质上就是select,poll,epoll这几个IO多路复用函数结合回调函数实现的架构。来看下一个简单的epoll服务器的逻辑是怎么样的:

//这里摘取了部分重要的代码来说明一个简单的epoll服务器的框架,如果想看完整的epoll服务器实现可以去我的github上看https://github.com/zk3326312/EpollServer
if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("socket error");
        return -1;
    }
 
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(default_port);
    serveraddr.sin_family = AF_INET;
 
 
    if( bind(listenfd,(sockaddr*)&serveraddr,sizeof(sockaddr)) == -1)
    {
        cout<<"bind error"<<endl;
        return -1;
    }
 
 
    if(listen(listenfd,listen_que_num) == -1)
    {
        cout<<"listen error"<<endl;
        return -1;
    }
 
    sp_Epoll->add(listenfd,EPOLLIN);
 
 
    while(true)
    {
        int nfds = sp_Epoll->wait(maxfdNUM,-1);
        for(int i = 0;i < nfds;i++)
        {
            if(sp_Epoll->_events[i].data.fd == listenfd)
            {
                if((confd = accept(listenfd,(sockaddr*)&clientaddr,&clilen)) == -1)
                {
                    cout<<"accept error"<<endl;
                    return -1;
                }
                char* str = inet_ntoa(clientaddr.sin_addr);
                cout<<"accept a connection of"<<str<<endl;
                sp_Epoll->add(confd,EPOLLIN);
            }
            else if((sp_Epoll->_events[i].events & EPOLLIN) == EPOLLIN)
            {
                int tempfd = sp_Epoll->_events[i].data.fd;
                if(tempfd < 0)
                {
                    continue;
                }
                char buffer[buffer_len];
                int  n = 0;
                if((n = recv(tempfd,buffer,buffer_len,0)) > 0)
                {
                    cout<< "receive message is"<< buffer<<endl;
                    buffer[n] = '\0';
                }
                else
                {
                    if(errno == ECONNRESET) //TCP connect over time,RST
                    {
                        close(tempfd);
                        sp_Epoll->_events[i].data.fd = -1;
            continue;
                    }
                    else if(errno == EINTR)
                    {
                        cout << "connect problem"<<endl;
                        continue;
                    }
                    else
                    {
                        close(tempfd);
                        sp_Epoll->_events[i].data.fd = -1;
            cout << "the connection is terminated by client"<<endl;
            continue;
                    }
                }
 
                if(strcmp(buffer,"quit") == 0)
                {
                    close(tempfd);
                    continue;
                }
                sp_Epoll->mod(confd,EPOLLOUT);
            }
            else if((sp_Epoll->_events[i].events & EPOLLOUT) == EPOLLOUT)
            {
                int tempfd = sp_Epoll->_events[i].data.fd;
                send(tempfd,"success recv your message",25,0);
                //sp_Epoll->del(tempfd);
                sp_Epoll->mod(tempfd,EPOLLIN);
            }
        }
    }
 
 
    return 0;
}

初看nginx的代码,又多又杂,到处都是回调和宏,很容易让人无从下手(看不懂真的不怪你),但是当你牢牢记住事件驱动这个本质时,你就会清晰很多了,整个过程无非就是连接到了,注册读写事件到epoll中,然后等fd就绪了再处理嘛,好好消化一下,下一篇文章我们就要正式开始编写nginx的helloWorld功能了。


--------------------- 
作者:曾柯 
来源:CSDN 
原文:https://blog.csdn.net/zk3326312/article/details/80724297 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值