嵌入式学习日记(36)TCP并发服务器构建——epoll

select特点:

  1. 使用位图(数组)实现对文件描述符集合的保存,最多允许同时监测1024个文件描述符;

  2. 需要应用和内核层的反复数据(文件描述符集合表)拷贝;

  3. 返回的集合表需要遍历寻找到达的事件;

  4. 只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。

poll特点:

  1. 使用链表实现对文件描述符集合的保存,没有了监测的文件描述符上限限制;

  2. 需要应用和内核层的反复数据(文件描述符集合表)拷贝;

  3. 返回的集合表需要遍历寻找到达的事件;

  4. 只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。

epoll特点:

  1. 使用红黑树(二叉树)实现文件描述符集合的存储,没有文件描述符上限限制,提高查找效率;

  2. 将文件描述符集合创建在内核层,避免了应用层和内核层的反复数据拷贝;

  3. 返回的是到达事件,不需要遍历,只需要处理事件即可;

  4. 可工作在水平触发模式(低速模式),也可工作在边沿触发模式(高速模式)。

epoll构建流程:

  1. 创建文件描述符集合 : int epoll_create(int size);

  2. 添加关注的文件描述符:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  3. epoll通知内核开始进行事件监测 :int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

  4. epoll返回时,获取到达事件的结果

  5. 根据到达事件做任务处理

int epoll_create(int size);

功能:通知内核创建文件描述符集合

参数:

size:监测的文件描述符个数

返回值:

成功:文件描述符(代表内核创建的集合)

失败:-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:对epoll的文件描述符集合进行操作

参数:

epfd:创建的epoll集合

op:对文件描述符集合进行的操作

EPOLL_CTL_ADD : 添加文件描述符到集合

EPOLL_CTL_MOD : 修改集合中的文件描述符

EPOLL_CTL_DEL :删除集合中的文件描述符

fd:要操作的文件描述符

event:文件描述符对应的事件

typedef union epoll_data {

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event {

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

events:文件描述符的事件:

EPOLLIN: 读事件

EOPLLOUT:写事件

data.fd : 关注的文件描述符

返回值:

成功:0

失败:-1

int epoll_wait(int epfd, struct epoll_event *events,

int maxevents, int timeout);

功能:通知内核开始监测文件描述符的事件

参数:

epfd:监测的文件描述符集合

events:保存返回的到达事件的结果(数组)

struct epoll_event evsMAX_FD_CNT

evs;

maxevents:最大的事件个数

timeout:监测的超时时间

-1 :不设置超时(一直阻塞)

返回值:

成功:到达的事件的个数

失败:-1

利用epoll构建并发服务器

cs 复制代码
#include "head.h"

#define SER_PORT 50001
#define SER_IP "192.168.0.180"
int n = 1;

int init_tcp_ser()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error");
        return -1;
    }
    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SER_PORT);
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);
    int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (ret < 0)
    {
        perror("bind error");
        close(sockfd);
        return -1;
    }
    ret = listen(sockfd, 5);
    if (ret < 0)
    {
        perror("listen error");
        close(sockfd);
        return -1;
    }
    return sockfd;
}

int epoll_fd_add(int epfds, int fd, uint32_t events)
{
    struct epoll_event ev;
    ev.events = events;
    ev.data.fd = fd;
    int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
    if (ret < 0)
    {
        perror("add error");
        return -1;
    }
    return 0;
}

int main()
{
    struct sockaddr_in cliaddr;
    socklen_t clilen = sizeof(cliaddr);
    int sockfd = init_tcp_ser();
    if (sockfd < 0)
    {
        return -1;
    }
    int epfds = epoll_create(n);
    if (epfds < 0)
    {
        perror("epoll_create error");
        return -1;
    }
    epoll_fd_add(epfds, sockfd, EPOLLIN);
    struct epoll_event evs[1024];
    char buff[1024] = {0};
    while (1)
    {
        int count = epoll_wait(epfds, evs, n, -1);
        if (count < 0)
        {
            perror("epoll_wait error");
            return -1;
        }
        for (int i = 0; i < count; ++i)
        {
            if (evs[i].data.fd == sockfd)
            {
                int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
                if (connfd < 0)
                {
                    perror("accept error:");
                    return -1;
                }
                epoll_fd_add(epfds, connfd, EPOLLIN);
                ++n;
            }
            else
            {
                memset(buff, 0, sizeof(buff));
                ssize_t cnt = recv(evs[i].data.fd, buff, sizeof(buff), 0);
                if (cnt < 0)
                {
                    perror("receive error");
                    epoll_ctl(epfds, EPOLL_CTL_DEL, evs[i].data.fd,NULL);
                    close(evs[i].data.fd);
                    continue;
                }
                else if (cnt == 0)
                {
                    printf("offline\n");
                    epoll_ctl(epfds, EPOLL_CTL_DEL, evs[i].data.fd,NULL);
                    close(evs[i].data.fd);
                    break;
                }
                printf("%s\n", buff);
            }
        }
    }
    close(sockfd);
    return 0;
}
相关推荐
BreezeJiang12 小时前
从域名到 Node.js:一次云服务器请求经过了哪些层?
服务器
草莓熊Lotso14 小时前
【Redis 初阶】Set 类型深度解析:去重集合的运算能力与实战场景
linux·网络·数据库·windows·redis·tcp/ip·缓存
y = xⁿ1 天前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
Julien20041 天前
调查和解决 SELinux 问题
linux·运维·服务器·网络·学习方法
kekekzt1 天前
TCP协议的粘包问题介绍,IP分片,MTU,MSS,滑动窗口的概念及之间的关系
网络·网络协议·tcp/ip
HugoStudio_SWAN1 天前
【擦除重绘】C++ 控制台动画:弹跳 Logo DVD 屏保效果
开发语言·c++·学习·程序人生
2601_962074681 天前
自己编译RustDesk,并将自建ID服务器和key信息写入客户端
运维·服务器
大模型丫丫1 天前
Hermes Agent:轻量级智能体框架实战指南
大数据·运维·服务器
sakiko_1 天前
Swift学习笔记42-SwiftUI的属性包装器(讲解+面试)
笔记·学习·ios·swiftui·swift
mooooooooooye1 天前
2026 年跨平台 SSH 客户端怎么选?Xterminal、Termius、MobaXterm 谁更合适
服务器·网络·ssh