epoll詳解(四)-- LT模式實例

通過本文你會了解到:
1. epoll LT模式實例
2. 運行測試

約定
1. 格式爲 /**/ 的註釋對程序的主要流程進行說明
2. 格式爲 // 的註釋對程序的難懂語句進行說明

#include <stdio.h> // for printf()
#include <stdlib.h> // for exit()
#include <unistd.h> // for read() and write()
#include <sys/epoll.h> // for epoll
#include <sys/socket.h> // for epoll
#include <netinet/in.h> //struct sockaddr_in
#include <string.h> // memset
#include <fcntl.h> // fcntl
#include <errno.h> // errno

#define EPOLL_QUEUE_LEN 32 //監聽的最大連接數
#define BUF_SIZE 1024

static int listen_socket(int port)
{
    int fd;
    struct sockaddr_in sa;


    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
        perror("bind");
        close(fd);
        return -1;
    }

    if(listen(fd, 5) == -1) {
        perror("listen");
        close(fd);
        return -1;
    }

    return fd;
}

int main(int argc, char **argv)
{
    struct epoll_event ev;
    struct epoll_event events[EPOLL_QUEUE_LEN];
    int epfd;
    int sfd;
    int nfds;

    if(argc != 2) {
        printf("usage: %s [port]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /*創建 epoll 實例*/
    epfd = epoll_create(EPOLL_QUEUE_LEN);
    if(epfd == -1) {
        perror("epoll_create");
        exit(EXIT_FAILURE);
    }

    /*對輸入port進行監聽*/
    printf("server listen form port: %d\n", atoi(argv[1]));
    sfd = listen_socket(atoi(argv[1]));
    if(sfd == -1) {
        printf("listen_socket failed\n");
        exit(EXIT_FAILURE);
    }

    /*將server描述符添加到epoll中*/
    ev.data.fd = sfd;
    ev.events = EPOLLIN;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE);
    }

    /*主循環*/
    while(1) {
        int i;

        /*等待epoll實例中描述符有I/O事件發生*/
        nfds = epoll_wait(epfd, events, EPOLL_QUEUE_LEN, -1);
        for(i = 0; i < nfds; i++) {
            if(events[i].events & (EPOLLERR | EPOLLHUP)) {
                //EPOLLERR - 出現錯誤
                //EPOLLHUP - 客戶端提前關閉連接(close by peer)
                continue;
            }

            if(!(events[i].events & EPOLLIN)) { //不是IN操作
                continue;
            }

            if(sfd == events[i].data.fd) { 
                /*有客戶端連入server*/
                struct sockaddr_in in_addr;
                socklen_t in_len;
                int infd;

                infd = accept(sfd, (struct sockaddr *)&in_addr, &in_len);
                if(infd == -1) {
                    perror("accept");
                    continue;
                }

                ev.data.fd = infd;
                ev.events = EPOLLIN;
                if(epoll_ctl(epfd, EPOLL_CTL_ADD, infd, &ev) == -1) {
                    perror("epoll_ctl");
                    exit(EXIT_FAILURE);
                }
                printf("incoming client [fd=%d]\n", infd);
            } else {
                /*收到客戶端數據*/
                ssize_t cnt;
                char buf[BUF_SIZE];

                memset(buf, 0, sizeof(buf));
                cnt = read(events[i].data.fd, buf, sizeof(buf));
                if(cnt == -1) {
                    perror("read");
                } else if (cnt == 0) { //fd 被關閉
                    printf("remote close fd\n");
                } else {
                    printf("receive data: %s\n", buf);
                }

                printf("close client [fd=%d]\n", events[i].data.fd);
                close(events[i].data.fd);
            } 
        }
    }

    close(sfd);

    return 0;
}   

運行測試
應用linux nc網絡工具,詳細用法參見man nc。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章