服务器(epoll模式)

由于多线程或者多进程会消耗资源

线程或进程调度消耗CPU资源

 出现了I/O多路复用,I/O多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,Linux下实现I/O多路系统调用主要有slelect, poll和epoll

服务器(epoll模式)_第1张图片

 服务器(epoll模式)_第2张图片

 

EPOLL 

 创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检 测的文件描述符的信息(红黑树),红黑树的查找删除修改的效率很高,还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向 链表)。

 服务器(epoll模式)_第3张图片

 

服务器(epoll模式)_第4张图片

 服务器端

#include 
#include 
#include 
#include 
#include 
#include 


int main() {
    // 创建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in saddr;
    saddr.sin_port = htons(9999);
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 绑定
    bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    // 监听
    listen(lfd, 8);

    //调用epoll_create()创建一个epoll实例
    int epfd = epoll_create(100);

    //将监听的文件描述符相关的检测信息添加到epoll实例中
    struct epoll_event epev;
    epev.data.fd = lfd;
    //常见的EPOll事件   EPOLLIN-读事件 EPOLLOUT-写事件 EPOLLERR-错误事件
    epev.events = EPOLLIN;
    //lfd表示要检测的文件描述符
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);

    //一个用于保存变化的数组
    struct epoll_event epevs[1024];

    while(1) {
        //epfd:epoll实例对应的文件描述符
        //epevs,表示传出参数,保存了发送变化的文件描述符的信息
        //epoll_wait用于检测变化了的个数
        int ret = epoll_wait(epfd, epevs, 1024, -1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }
        printf("ret %d\n", ret);

        for (int i = 0; i < ret; i++) {
            int curfd = epevs[i].data.fd;

            if (curfd == lfd) {
                //监听的文件描述符有数据达到,表示有客户端连接
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t*)&len);

                epev.events = EPOLLIN;
                epev.data.fd = cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD, cfd, &epev);
            }else {
                //为了排除写事件
            if (epevs[i].events & EPOLLOUT) continue;

            // 有数据到达,需要通信,有数据到达和有客户端连接的含义不一样
            char buf[1024] = {0};
            int len = read(curfd, buf, sizeof(buf));
            if(len == -1) {
                perror("read");
                exit(-1);
            } else if(len == 0) {
                printf("client closed...\n");
                epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
                close(curfd);
            } else if(len > 0) {
                printf("read buf = %s\n", buf);
                write(curfd, buf, strlen(buf) + 1);
            }
            }
        }
    }

    close(lfd);
    close(epfd);
    return 0;
}

客户端

#include 
#include 
#include 
#include 
#include 

int main() {

    // 创建socket
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(9999);

    // 连接服务器
    int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));

    if(ret == -1){
        perror("connect");
        return -1;
    }

    int num = 0;
    while(1) {
        char sendBuf[1024] = {0};
        sprintf(sendBuf, "send data %d", num++);
        write(fd, sendBuf, strlen(sendBuf) + 1);

        // 接收
        int len = read(fd, sendBuf, sizeof(sendBuf));
        if(len == -1) {
            perror("read");
            return -1;
        }else if(len > 0) {
            printf("read buf = %s\n", sendBuf);
        } else {
            printf("服务器已经断开连接...\n");
            break;
        }
        // sleep(1);
        usleep(1000);
    }

    close(fd);

    return 0;
}

你可能感兴趣的:(Linux高薪,经验分享)