epoll

epoll的触发方式有水平触发以及边缘触发。

水平触发相当于高级的select,当可读或可写时就会触发。比如将fd设为可读并交给epoll,只要fd有数据可读都会触发

边缘触发是在由不可读变为可读一瞬间触发一次。

#define socket_t int

static void socket_call(const char *label, int result) {
    if (result < 0) {
        fprintf(stderr, "%s : %s", label, strerror(errno));
        abort();
    }
}

void set_nonblock(socket_t fd) {
//    int nb;
//    nb = 1;
//    return ioctl(fd, FIONBIO, &nb);
    int fl = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

socket_t startup(char* _ip, int _port)  //创建一个套接字,绑定,检测服务器
        {
    //sock
    //1.创建套接字
    socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
    socket_call("create socket", sock);

    int opt = 1;
    socket_call("set socket options",
            setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)));

    //2.填充本地 sockaddr_in 结构体(设置本地的IP地址和端口)
    struct sockaddr_in local;
    local.sin_port = htons(_port);
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr(_ip);

    //3.bind()绑定
    socket_call("bind socket",
            bind(sock, (struct sockaddr*) &local, sizeof(local)));
    //4.listen()监听 检测服务器
    socket_call("listen socket", listen(sock, 256));
    return sock;    //这样的套接字返回
}

int main(int argc, char **argv) {

    socket_t listen_sock = startup("127.0.0.1", 8081);

    int epfd = epoll_create(256);
    struct epoll_event _ev;
    _ev.events = EPOLLIN;
    _ev.data.fd = listen_sock;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &_ev);

    struct epoll_event revs[128];

    int timeout = -1;
    int num = 0;
    int done = 0;
    static int Con_Num = 0;
    static int Client_Write = 0;
    while (!done) {
        switch ((num = epoll_wait(epfd, revs, 128, timeout))) {
        case 0:
            printf("timeout
");
            break;
        case -1:
            socket_call("epoll_wait", num);
            break;
        default:

            for (int i = 0; i < num; i++) {
                int rsock = revs[i].data.fd;
//                printf("even type : %d
", revs[i].events);
                if (rsock == listen_sock && (revs[i].events & EPOLLIN)) {
                    printf("Client Connect Times : %d
", ++Con_Num);
                    struct sockaddr_in peer;
                    socklen_t len = sizeof(peer);
                    int new_fd = accept(listen_sock, (struct sockaddr*) &peer,
                            &len);
                    if (new_fd > 0) {
                        printf("get a new client from %s:%d ,give fd : %d
",
                                inet_ntoa(peer.sin_addr), ntohs(peer.sin_port),
                                new_fd);
                        /*
                         * 设为非阻塞的意义是什么?
                         */
                        set_nonblock(new_fd);
                        _ev.events = EPOLLIN | EPOLLET;
                        _ev.data.fd = new_fd;
                        epoll_ctl(epfd, EPOLL_CTL_ADD, new_fd, &_ev);
                    }

                } else {
                    if (revs[i].events & EPOLLIN) {
                        char buf[5];
                        ssize_t _s = read(rsock, buf, sizeof(buf) - 1);
                        if (_s > 0) {
                            printf("Client Write socket : %d times : %d
",
                                    rsock, ++Client_Write);
                            buf[_s] = '';
                            printf("Client Send buf :
%s", buf);
                            while (true) {
                                ssize_t _s = read(rsock, buf, sizeof(buf) - 1);
                                if(_s<0){
                                    printf("%s
",strerror(errno));
                                    break;
                                }
                                buf[_s] = '';
                                printf("%s", buf);
                            }
                            printf("
");
                        } else if (_s == 0) {
                            printf("Client :%d Close
", revs[i].data.fd);
                            epoll_ctl(epfd, EPOLL_CTL_DEL, rsock, NULL);
                            close(rsock);
                        }

                    }
                }
            }

        }
    }

}

为什么需要将socketfd设为非阻塞呢?

因为如果一个客户端连接的socketfd连接时,客户端写入数据,需要保证客户端的数据完全读取完毕,那么就嵌套一个循环一直read,如果阻塞会在读完数据后阻塞住,而非阻塞则会返回<0且errno变为EAGIN。

少壮不识cpp,老大方知cpp可怕
原文地址:https://www.cnblogs.com/Jacket-K/p/8377744.html