epoll简单服务器

4 篇文章 0 订阅

/*
	epoll三个重要函数, 底层红黑树
	1.int epoll_create(int size);  创建 epoll 树的根节点
		size:理论上最大数的节点个数,超出是会自动扩充 
	2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 对 epoll 树的操作 添加/删除/修改
		op: EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD
		fd: 文件描述符
		*event: 连接到epoll树的文件描述符fd的事件
		
		typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    
               epoll_data_t data;       
           };
        data:所选择的文件描述符
		events:
			EPOLLIN:读
			EPOLLOUT:写
			EPOLLET:边沿非阻塞
			
 	3.int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);  委托内核检测
          maxevents:委托检测个数
          *events:传出参数是个数组, 得到所检测的结果会存放在一个数组里面

*/



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    if(argc < 2)
    {
        printf("./a.out port\n");
        exit(1);
    }

    // 创建socket
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd == -1)
    {
        printf("socket err");
        exit(1);
    }
    
    // 设置端口复用
    int flags = 1;
    int opt = setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &flags, sizeof(flags));
    if(opt == -1)
    {
        perror("setsockopt err");
        exit(1);
    }

    // 绑定 服务端 IP 和 port struct sockaddr_in server;
    struct sockaddr_in server;
    memset(&server, 0x00, sizeof(server));
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[1]));
    int ret = bind(sfd, (struct sockaddr *)&server, sizeof(server));
    if(ret == -1)
    {
        perror("bind err");
        exit(1);
    }
    
    // 设置监听
    ret = listen(sfd, 128);
    if(ret == -1)
    {
        perror("listen err");
        exit(1);
    }
    
    // 客户端连接以及数据发送请求处理
    struct sockaddr_in client;
    socklen_t client_addr_len = sizeof(client);
    // 创建eopll 树的根节点
    int epfd = epoll_create(3000);
    // 初始化 epoll 树
    struct epoll_event ev;
    ev.data.fd = sfd;
    ev.events = EPOLLIN;
    // 将服务端添加进 ePoll 树
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);
    if(ret == -1)
    {
        perror("epoll_ctl err");
        exit(1);
    }
    struct epoll_event all[3000];

    // epoll 与 客户端 连接、消息数据处理.
    while(1)
    {   
        // 委托 epoll 检查
        int epno = epoll_wait(epfd, all, sizeof(all) / sizeof(all[0]), -1);
        if(epno == -1)
        {
            perror("epoll_wait err");
            exit(1);
        }
        // 遍历 委托检查返回的个数
        for(int i = 0; i < epno; ++i)
        {
            
            int fd = all[i].data.fd;
            // 判断是否有 新连接
            if(fd == sfd)
            {
                printf("与客户端连接成功\n");
                int cfd = accept(sfd, (struct sockaddr *)&client, &client_addr_len);
                if(cfd == -1)
                {
                    perror("accept err");
                    exit(1);
                }
                // 设置非阻塞模式
                int flag = fcntl(cfd, F_GETFL);
                flag |= SOCK_NONBLOCK;
                fcntl(cfd, flag);

                struct epoll_event temp;
                temp.data.fd = cfd;
                // 非阻塞边沿模式
                temp.events = EPOLLIN | EPOLLET;
                // 将得到的新连接 加入 epoll树
                ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
                if(ret == -1)
                {
                    perror("epoll_ctl err + 95");
                    exit(1);
                }
            }
            // 客户端是否发送数据
            else
            {
                // 只进行 读 事件
                if(!(all[i].events & EPOLLIN))
                {
                    continue;
                }

                // 接受客户端发送的数据
                char buf[BUFSIZ] = {0};
                ret = recv(fd, &buf, sizeof(buf), 0);
                if(ret == -1)
                {
                    perror("recv err");
                    exit(1);
                }
                else if(ret == 0)
                {
                    printf("与客户端连接断开\n");
                    // 将 客户端 从 epoll 树删除
                    ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
                    if(ret == -1)
                    {
                        perror("epoll ctl del err");
                        exit(1);
                    }
                    close(fd);
                }
                else
                {
                    printf("recv the data %s", buf);
                    send(fd, buf, ret, 0);
                }

            }
        }
    }
    close(sfd);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值