美文网首页
Epoll介绍

Epoll介绍

作者: 诺之林 | 来源:发表于2018-09-26 12:49 被阅读25次

目录

入门

引入

  • 与多进程/线程相比 I/O多路复用的优势是系统开销小

关于更多I/O多路复用 可以参考NIO服务

介绍

  • epoll是Linux下多路复用I/O接口select/poll的增强版本

优点

  • 支持一个进程打开大数目的socket描述符 - 更高并发

  • I/O效率不随socket描述符增加而线性下降 - 更好性能

Client

vim client.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 40

int main(int argc, char *argv[])
{
    int client_sockfd;              // 客户端套接字
    struct sockaddr_in client_addr; // 服务器端网络地址结构体

    // Socket Step1: 创建客户端套接字
    memset(&client_addr, 0, sizeof(client_addr));         // 数据初始化
    client_addr.sin_family = AF_INET;                     // 设置为IP通信
    client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址
    client_addr.sin_port = htons(8000);                   // 服务器端口号
    if ((client_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("client socket creation failed");
        exit(EXIT_FAILURE);
    }
    // Socket Step2: 将套接字绑定到服务器的网络地址上
    if (connect(client_sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("connect to server failed");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        char buf[BUFFER_SIZE];
        printf("Please input the message:");
        scanf("%s", buf);

        // 处理"exit"消息
        if (strcmp(buf, "exit") == 0)
        {
            break;
        }

        send(client_sockfd, buf, BUFFER_SIZE, 0);

        // 接收服务器端信息
        int len = recv(client_sockfd, buf, BUFFER_SIZE, 0);
        printf("receive from server:%s/n", buf);
        if (len < 0)
        {
            perror("receive from server failed");
            exit(EXIT_FAILURE);
        }
    }

    close(client_sockfd);
    return 0;
}

关于socket更多介绍 可以参考linux socket编程总结

Server

vim server.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define BUFFER_SIZE 40
#define MAX_EVENTS 10

int main(int argc, char *argv[])
{
    int server_sockfd;              // 服务器端套接字
    int client_sockfd;              // 客户端套接字
    struct sockaddr_in server_addr; // 服务器网络地址结构体
    struct sockaddr_in client_addr; // 客户端网络地址结构体

    // Socket Step1: 创建服务器端套接字
    memset(&server_addr, 0, sizeof(server_addr)); // 数据初始化
    server_addr.sin_family = AF_INET;             // 设置为IP通信
    server_addr.sin_addr.s_addr = INADDR_ANY;     // 服务器IP地址 - 允许连接到所有本地地址上
    server_addr.sin_port = htons(8000);           // 服务器端口号
    if ((server_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return 1;
    }
    // Socket Step2: 将套接字绑定到服务器的网络地址上
    if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind");
        return 1;
    }
    // Socket Step3: 监听连接请求
    listen(server_sockfd, 5);

    // Epoll Step1: 创建一个epoll句柄
    int epoll_fd;
    epoll_fd = epoll_create(MAX_EVENTS);
    if (epoll_fd == -1)
    {
        perror("epoll_create failed");
        exit(EXIT_FAILURE);
    }
    // Epoll Step2: 向epoll注册server_sockfd监听事件
    struct epoll_event ev;                 // epoll事件结构体
    struct epoll_event events[MAX_EVENTS]; // 事件监听队列
    ev.events = EPOLLIN;
    ev.data.fd = server_sockfd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_sockfd, &ev) == -1)
    {
        perror("epll_ctl:server_sockfd register failed");
        exit(EXIT_FAILURE);
    }
    while (1)
    {
        // Epoll Step3: 等待事件发生
        int nfds;
        nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds == -1)
        {
            perror("start epoll_wait failed");
            exit(EXIT_FAILURE);
        }
        int i;
        for (i = 0; i < nfds; i++)
        {
            // 客户端有新的连接请求
            if (events[i].data.fd == server_sockfd)
            {
                // 等待客户端连接请求到达
                int sin_size;
                sin_size = sizeof(struct sockaddr_in);
                if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &sin_size)) < 0)
                {
                    perror("accept client_sockfd failed");
                    exit(EXIT_FAILURE);
                }
                // 向epoll注册client_sockfd监听事件
                ev.events = EPOLLIN;
                ev.data.fd = client_sockfd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_sockfd, &ev) == -1)
                {
                    perror("epoll_ctl:client_sockfd register failed");
                    exit(EXIT_FAILURE);
                }
                printf("accept client: %s\n", inet_ntoa(client_addr.sin_addr));
            }
            // 客户端有数据发送过来
            else
            {
                char buf[BUFFER_SIZE];
                int len = recv(client_sockfd, buf, BUFFER_SIZE, 0);
                if (len < 0)
                {
                    perror("receive from client failed");
                    exit(EXIT_FAILURE);
                }
                printf("receive from client: %s\n", buf);
                send(client_sockfd, buf, len, 0);
            }
        }
    }
    return 0;
}

关于epoll接口定义 可以参考epoll.h

Test

docker run -it --name epoll-demo ubuntu:16.04 /bin/sh

apt update && apt install -y build-essential
docker cp server.c epoll-demo:/root && docker cp client.c epoll-demo:/root

docker exec -it epoll-demo /bin/sh
cd ~

gcc -o server server.c && gcc -o client client.c

关于gcc更多介绍 可以参考gcc命令

./server
accept client: 127.0.0.1
receive from client: 1
accept client: 127.0.0.1
receive from client: 2
./client
# Please input the message:1
# receive from server:1/nPlease input the message:
./client
# Please input the message:2
# receive from server:2/nPlease input the message:
ps -ax | grep server | awk '{print $1}' # 100

cat /proc/100/status | grep Threads # Threads:  1

参考

相关文章

网友评论

      本文标题:Epoll介绍

      本文链接:https://www.haomeiwen.com/subject/nzcdoftx.html