손 으로 nginx 로 자신의 서버 를 개발 하 는 것 을 가 르 칩 니 다 --- nginx 를 이용 하여 hello World 프로그램 을 개발 합 니 다 (1)

6252 단어
nginx 를 배 울 수 있 는 당신 은 많은 코드 를 훑 었 을 것 입 니 다. 당신 의 학습 코드 가 모두 hello World 에서 시작 되 었 다 고 믿 습 니 다. 그러면 오늘 우 리 는 nginx 로 hello World 를 개발 하 겠 습 니 다. 우리 가 실현 하고 자 하 는 기능 은 브 라 우 저가 당신 의 서버 를 방문 할 때 당신 의 끝 에 hello World 를 인쇄 하 는 것 입 니 다.
코드 를 급히 훑 어보 지 말고 왜 이 칼럼 을 쓰 고 싶 은 지 먼저 이야기 하 세 요. 사실 본인 도 서버 개발 초보 입 니 다. 나이 가 조금 들 면 반응 과 기억력 이 많이 떨 어 지 는 것 같 습 니 다. 뭔 가 를 써 서 기억 할 수 있 도록 도와 야 합 니 다.이 칼럼 은 많은 nginx 초보 자 들 이 nginx 를 조금 더 깊이 이해 하 는 데 도움 이 될 것 입 니 다. 제 모든 글 은 길지 않 지만 중요 한 점 이 있 습 니 다. 만약 에 저 를 따라 계속 공부 하면 nginx 로 자신의 웹 서버 를 구축 하 는 방법 을 배 울 것 입 니 다. nginx 로 자신의 역방향 프 록 시 서버 를 구축 하 는 것 을 배 울 것 입 니 다. 그리고 만약 에 당신 이 충분히 진지 하 다 면...nginx 의 각종 고급 특성, 예 를 들 어 메모리 정렬 이 가 져 온 고 효율, slab 알고리즘, 각종 풀 (메모리 풀, 연결 풀) 에 대해 서도 어느 정도 알 수 있 습 니 다.nginx 를 설명 하 는 동시에 저 는 nginx 중의 많은 네트워크 특성 과 결합 하여 기 존의 최첨단 지식 을 설명 할 것 입 니 다.예 를 들 어 http 2.0, tles 1.3 등 제 글 에 오류 가 있 는 것 을 발견 하면 여러분 의 지적 을 환영 합 니 다.
본 격 적 으로 nginx 를 시작 하기 전에 다음 echo 서버 가 어떻게 했 는 지 기억 합 니 다.
#include 
#include 
#include 
#include 
#include 

#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 재 활용 과 각종 디자인 모델 을 이용 하여 이 몇 개의 인 터 페 이 스 를 봉인 했다.서버 개발 을 공부 할 때 개인 적 으로 이 본질 을 파악 해 야 한다 고 생각 합 니 다.
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<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<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<_events[i].data.fd = -1;
			continue;
                    }
                    else if(errno == EINTR)
                    {
                        cout << "connect problem"<_events[i].data.fd = -1;
			cout << "the connection is terminated by client"<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 의 hello World 기능 을 본 격 적 으로 작성 해 야 합 니 다.

좋은 웹페이지 즐겨찾기