tcp使用epoll进行实现并发

本文详细介绍了如何利用Epoll的ET和LT模式优化TCP服务器的并发管理,通过Epoll创建、添加事件、等待事件的方式处理连接请求和数据传输。文中展示了将监听套接字和连接套接字的事件处理分离,使用回调函数实现更灵活的事件处理,进一步讨论了Reactor模型的应用,以及如何通过双Epoll优化连接处理效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tcp使用epoll进行实现并发

tcp 服务器编写的步骤都是很熟悉的了。tcp由于每一个新的连接都会新建一个socket和客户端进行通信,但是新建立的连接在很多次之后就会管理就会出现问题,这个时候就可以使用epoll进行管理。
epoll是一种多路转接io,相比selete和poll在管理大量描述符的时候优势很明显。具体的优势我们后面可以慢慢说。
epoll的流程, 创建epoll描述符–> 添加事件–> wait;

 int epoll_create (int __size)
 __size: 这个值在早期时候用于确定返回列表的预留长度,现在由于返回的内容放置在一个双向链表中,这个		  实际上已经没什么作用了
 return 返回新创建的epoll描述符
extern int epoll_ctl (int __epfd, int __op, int __fd,
		      struct epoll_event *__event) 
__epfd: epoll的文件描述符
_op	:	EPOLL_CTL_ADD 添加监视节点, EPOLL_CTL_DEL 删除监视 , EPOLL_CTL_MOD 修改监视
__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 */
} 
extern int epoll_wait (int __epfd, struct epoll_event *__events,
		       int __maxevents, int __timeout);
参数分别为: epoll文件描述符,返回事件存储的位置,数组的最大长度。超时时间, -1 阻塞等待有事件就返回,0不管有无事件都直接返回 , 大于0, 等待·超时时间。
return 小于零 出错, 大于0 事件发生的个数

epoll的两种模式:
ET:边沿触发, 只触发一次 , 只有文件描述符从不可读变为可读的时候才会被触发, EPOLLET 这个宏用于设置ET模式
LT:水平触发, 一直触发
这两种模式都是在调用epol_ctl添加事件之前进行设置的,epoll默认是使用LT模式,在高版本中的系统中没用提供设置LT模式的宏。

这个触发是什么意思呢?

假如说使用epoll监控tcp的listen套接字并且监控可读事件(可读事件代表有新的连接建立),如果我们设置LT模式,当新的连接到来的时候epoll的返回的事件数组中肯定会有listen描述符告诉我们可读事件发生了。但是此时如果我们没用获取新的连接,那么返回的文件描述符中还会有listen描述符。
如果是ET模式那么在返回的事件列表中就会没有。

可以看下述代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <error.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>


int main(int argc, char *argv[]){
   
    int socket_fd  = socket(AF_INET, SOCK_STREAM, 0); //创建流套接字
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));

    addr.sin_family = AF_INET; //使用IPV4
    addr.sin_port = htons(8888); //绑定端口 8888
    addr.sin_addr.s_addr = INADDR_ANY; //绑定本机的所有网卡

    if(bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0){
   
        exit(-1);
    }

    /*全连接队列为 5 或者 全连接和半连接和为5 和版本有关*/
    if(listen(socket_fd, 5) < 0){
   
        exit(-1);        
    }

    int epfd = epoll_create(1);//只有0和1的区别,和之前epool实现有关系
    struct epoll_event ev, event[1024];
    ev.events = EPOLLIN; //监控可读事件
    ev.data.fd = socket_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, socket_fd, &ev); //添加事件

    while(1){
   
        int nready = epoll_wait(epfd, event, 1024, -1); //fd和过期时间, -1 相当于有数据就返回, 
        if(nready < -1){
                                   // 0那么就直接返回,  返回值为 正数字,
            break;                                      // 那么就是所有的事件
        }

        char buff[1024] = {
   0};
        for(int i = 0 ; i < nready; i++){
    //遍历返回事件
            if(event[i].data.fd == socket_fd){
    //如果是监听描述符那么就建立新的连接
                struct sockaddr_in addrs_client;
                int clinet_size = sizeof(struct sockaddr_in);
                int clientfd = accept(socket_fd, (struct sockaddr *)&addrs_client, (socklen_t*)&clinet_size);
                if(clientfd < 0){
    //连接建立失败
                    continue;
                }
                    //默认是LT , 没有定义EPOLLT, 设置为LT模式
                ev.events = EPOLLIN;   //EPPOLL 默认是LT, LT是水平触发,有数据就会一直触发。ET是边缘触发,是从没有数据变成有数据才 
               // ev.events = EPOLLIN | EPOLLET; // EPOLLET 设置为ET模式
                ev.data.fd = clientfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev); //将获取到的新连接加入到epoll中
            }else{
   
                int clientfd = event[i].data.fd; 	//获取一个连接
                int ret = recv(clientfd, buff, 1024, 0); //非阻塞如果没有数据那么就返回-1
                if(ret < 0){
   //现在是不可能-1的,但是如果一直while读,读空了就会返回-1
                    if(errno == EAGAIN || errno ==</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值