epoll

一、相关函数说明

int epoll_create(int size);

函数说明:

创建接口,返回epoll的操作句柄

参数说明:

size:不能传递负数,传递整数的情况下参数无意义

调用示例:

epoll_fd_ = epoll_create(5);

epoll事件集合:

struct epoll_event

{

    uint32_t eventes;

//epoll事件,EPOLLIN(可读事件),EPOLLOUT(可写事件)

epoll_data_t data;

//存放该事件结构对应的文件描述符或存放文件描述符结构的地址   

}

epoll_data_t结构:

typedef union epoll_data

{

    viod *ptr;//ptr指向的空间中要有fd的值

    int fd;//设置为要监控的文件描述符

}epoll_data_t;

epoll_data_t是联合,成员变量(ptr/fd)公用内存,所以一次只使用一个保存文件描述符

typedef:为现有类型起个别名

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

函数说明:

用于对文件描述符的增删改

参数说明:

epfd:epoll的操作句柄

op:

    (1)EPOLL_CTL_ADD:添加一个文件描述符的事件结构到epoll当中

    (2)EOPOLL_CTL_MOD:修改一个文件描述符对应的事件结构

    (3)EPOLL_CTL_DEL:从epoll当中删除一个文件描述符对应的事件结构

fd:要被处理的文件描述符

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

调用示例:

//添加一个文件描述符

 struct epoll_event ee;

 ee.events = EPOLLIN;

 ee.data.fd = newsockfd;

 epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, newsockfd, &ee);

int epoll_wait(int epfd,struct epoll_event* event,int maxevents,int timeout);

函数说明:

等待接口,获取就绪的文件描述符,返回就绪的文件描述符个数

参数说明:

epfd:epoll的操作句柄

events:事件结构集合,从epoll中获取就绪的事件结构(从内核的双向链表中拷贝。

maxevents:一次最多获取的事件结构

timeout

    (1)>0:带有超时事件

    (2)==0:非阻塞

    (3)<0:阻塞

调用示例:

struct epoll_event arr[10];

int ret = epoll_wait(cs->epoll_fd_, arr, sizeof(arr)/sizeof(arr[0]), -1);

epoll的工作模式

LT(epoll默认的触发方式):水平触发(只要达到触发事件的条件,就进行触发(通知),如果事件没有被处理完,不会进行新一轮监听,还会继续触发该事件)

ET:边缘触发(当达到事件触发条件,只会触发(通知)一次,需要设置非阻塞,搭配循环使用)

二、用epoll实现服务端和多个客户端的通信(tcp)

    1.服务端

/*
 * 1.创建侦听套接字
 * 2.绑定地址信息
 * 3.监听
 * 4.获取新连接套接字
 * 5.创建epoll操作句柄
 * 6.添加侦听套接字
 * 7.循环处理
 *     *调用epoll_wait
 *     *分情况处理根据套接字类型不同进行不同处理(侦听套接字, 新连接套接字)
 */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

int main(){
    //创建侦听套接字
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }

    //绑定地址信息
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(39898);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    //监听
    listen(listen_sockfd, 5);

    //创建接口,返回epoll操作句柄
    int epfd = epoll_create(10);
    if(epfd < 0){
        perror("epoll_create");
        return 0;
    }
    
    //将侦听套接字加入epoll
    struct epoll_event ee;
    ee.events = EPOLLIN;
    ee.data.fd = listen_sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sockfd, &ee);

    while(1){
        //1. 调用epoll_wait,等待就绪的文件描述符
        struct epoll_event arr[10];
        int ret = epoll_wait(epfd, arr, sizeof(arr)/sizeof(arr[0]), -1);
        if(ret < 0){
            continue;
        }

        //2. 对就绪的文件描述符进行分情况处理(侦听套接字, 新连接套接字)
        for(int i = 0; i < ret; i++){
            if(listen_sockfd == arr[i].data.fd){
                //处理侦听套接字
                /*
                 * 1. accept接收新连接,返回新连接套接字
                 * 2. 添加新连接套接字到epoll当中
                 * */
                int newsockfd = accept(arr[i].data.fd, NULL, NULL);
                if(newsockfd < 0)
                {
                    continue;
                }

                struct epoll_event eee;
                eee.events = EPOLLIN;
                eee.data.fd = newsockfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, newsockfd, &eee);
            }
            else
            {
                //处理新连接套接字
                char buf[1024] = {0}; 
                size_t r_size = recv(arr[i].data.fd, buf, sizeof(buf) - 1, 0);
                if(r_size < 0)
                {
                    continue;
                }
                else if(r_size == 0)
                {
                    /*对端将连接关闭了
                     * 1.从epoll当中移除该文件描述符
                     * 2.关闭文件描述符
                     */
                    epoll_ctl(epfd, EPOLL_CTL_DEL, arr[i].data.fd, NULL);
                    close(arr[i].data.fd);
                    continue;
                }

                printf("[recv from %d, msg is ] %s\n", arr[i].data.fd, buf);

                memset(buf, '\0', sizeof(buf));
                snprintf(buf, sizeof(buf), "%d, i am server", arr[i].data.fd);
                send(arr[i].data.fd, buf, strlen(buf), 0);
            }
        }
    }

    return 0;
}

                       2.客户端

/*客户端
 *1.创建套接字
 *2.发起连接
 *3。收发数据
*/
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
    //创建套接字
    int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sockfd<0)
    {
        perror("socket");
        return 0;
    }
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(38989);
    addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    //发起连接
    int ret=connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret<0)
    {
        perror("connect");
        return 0;
    }
    while(1)
    {
        //收发数据
        char buf[1024]={0};
        printf("please enter:");
        fflush(stdout);
        std::cin>>buf;
        send(sockfd,buf,strlen(buf),0);
        memset(buf,'\0',1024);
        ssize_t recv_size=recv(sockfd,buf,sizeof(buf)-1,0);
        if(recv_size<0)
        {
            perror("recv");
            return 0;
        }
        else if(recv_size==0)
        {
            close(sockfd);
            return 0;
        }
        else
        {
            printf("buf is:%s\n",buf);
        }
    }
    //关闭套接字
    close(sockfd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值