io_uring实现Procator

上一次我们用epoll实现了reactor模式,这一次我们用io_uring来实现Proactor模式的tcp服务器。我们已经可以达到用一个线程去处理大量的连接。但此时这个线程每处理一个I/O,线程会一直等待(就是我们常说的阻塞I/O或者同步I/O下边有解释),是不是有点傻瓜?是否可以让线程多工作,别傻傻的等待。当然可以!这就是今天我们所要介绍的目前常用的Proactor模式。

阻塞I/O(同步I/O):I/O操作按照顺序执行,并且每一步都需要等待上一步完成。
非阻塞I/O(异步I/O):不需要等待特定任务完成,可以并行执行其他任务,并通过回调函数或事件通知机制处理结果。
举个例子,你想要出去买份饭带回家吃,假设只有这有两种方式。第一种方式这时候你可以先在手机上下单,然后等一段时间后去饭店拿饭,等待的期间呢,你可以去洗衣服(处理其他事情),这种就是非阻塞I/O。
还有一种方式就是,你直接去饭店,到那之后跟老板说要一份饭,打包带走,到饭做完打包好的期间呢,你不能去做其他事情,只能在饭店傻傻等待,这种就类似于阻塞I/O。

Proactor和Reactor的区别:

Proactor和Reactor是两种常见的并发模式,用于处理I/O操作的异步编程。

Reactor模式基于事件驱动,在一个主循环中等待多个I/O事件,并根据不同类型的事件调用相应的处理程序。Reactor负责监听和分发事件,但实际的I/O操作仍然由应用程序自己完成。

Proactor模式则更进一步,它将实际的I/O操作也交给框架来完成,应用程序只需提供数据和回调函数。在Proactor模式下,当一个I/O操作完成时,框架会自动触发相应的回调函数进行后续处理。

因此,区别可以总结如下:

  • Reactor模式中,应用程序负责执行实际的I/O操作;而Proactor模式中,框架负责执行实际的I/O操作。
  • Reactor模式是同步IO(阻塞IO);而Proactor模式是异步IO(非阻塞IO)。
  • Reactor模式中,应用程序需要主动检查事件并进行处理;而Proactor模式中,框架会主动通知应用程序。

选择使用哪种模式取决于具体需求和场景。如果需要更大程度上解放应用程序对I/O操作的控制,并充分利用异步非阻塞特性,则可以考虑使用Proactor模式。如果对于同步IO已经能够满足需求,或者对于I/O操作的控制要求更高,则可以选择Reactor模式。

//io_uring中的liburing库安装可以参考这篇文章
io_uring之liburing库安装_liburing 需要安装吗-CSDN博客

//编译 需要从gitlab上下载liburing库
gcc -o uring_tcp_server uring_tcp_server.c -lpthread -I /home/xxxx(看你自己的安装路径)/liburing/src/include/liburing -luring

实现代码:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <liburing.h>
#include <netinet/in.h>
#include <unistd.h>

//io_uring实现tcp服务器

#define EVENT_ACCEPT 0
#define EVENT_READ   1
#define EVENT_WRITE  2

struct conn_info
{
    int fd;
    int event;
};

int init_server(unsigned short port)
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_port = htons(port);
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(-1 == bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(struct sockaddr)))
    {
        perror("bind");
        return -1;
    }
    listen(sockfd,10);
    return sockfd;

}

#define ENTRIES_LENGTH  1024
#define BUFFER_LENGTH 1024

int set_event_recv(struct io_uring *ring,int sockfd, void *buf, size_t len, int flags)
{
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    
    struct conn_info accept_info ={
        .fd = sockfd,
        .event = EVENT_READ,
    };

    io_uring_prep_recv(sqe,sockfd,buf,len,flags);
    memcpy(&sqe->user_data,&accept_info,sizeof(struct conn_info));
}

int set_event_send(struct io_uring *ring,int sockfd, void *buf, size_t len, int flags)
{
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    
    struct conn_info accept_info ={
        .fd = sockfd,
        .event = EVENT_WRITE,
    };

    io_uring_prep_send(sqe,sockfd,buf,len,flags);
    memcpy(&sqe->user_data,&accept_info,sizeof(struct conn_info));
}


int set_event_accept(struct io_uring *ring,int fd,struct sockaddr *addr,
socklen_t *addrlen,int flags)
{
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    
    struct conn_info accept_info ={
        .fd = fd,
        .event = EVENT_ACCEPT,
    };

    io_uring_prep_accept(sqe,fd,(struct sockaddr*)addr,addrlen,flags);
    memcpy(&sqe->user_data,&accept_info,sizeof(struct conn_info));
}




//实现tcpserver
int main(int argc,char *argv[])
{
    unsigned short port = 9999;
    int sockfd = init_server(port);

    struct io_uring_params params;
    memset(&params,0,sizeof(params));

    struct io_uring ring;
    io_uring_queue_init_params(ENTRIES_LENGTH,&ring,&params);

    //struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
#if 0 
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    accept(sockfd,(struct sockaddr*)&clientaddr,&len);
#else
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    set_event_accept(&ring,sockfd,(struct sockaddr*)&clientaddr,&len,0);
    // struct conn_info accept_info ={
    //     .fd = sockfd,
    //     .event = EVENT_ACCEPT,
    // };


    // io_uring_prep_accept(sqe,sockfd,(struct sockaddr*)&clientaddr,&len,0);
    // memcpy(&sqe->user_data,&accept_info,sizeof(struct conn_info));

#endif
    char buffer[BUFFER_LENGTH] = {0};
    while(1)
    {
       
        io_uring_submit(&ring);

 
        struct io_uring_cqe *cqe;
        io_uring_wait_cqe(&ring,&cqe);
        


        struct io_uring_cqe *cqes[128];
        int nready = io_uring_peek_batch_cqe(&ring,cqes,128);//epoll_wait

        int i = 0;
        for(i=0;i<nready;i++)
        {
            struct io_uring_cqe *entries = cqes[i];
            struct conn_info result;
            memcpy(&result,&entries->user_data,sizeof(struct conn_info));

            if(result.event == EVENT_ACCEPT)
            {
                set_event_accept(&ring,sockfd,(struct sockaddr*)&clientaddr,&len,0);
                printf("set_event_accept\n");

                int connfd = entries->res;

                set_event_recv(&ring,connfd,buffer,BUFFER_LENGTH,0);

            }
            else if(result.event == EVENT_READ)
            {
                int ret = entries->res;
                printf("set_event_recv ret:%d,buffer:%s\n",ret,buffer);

                if(ret == 0)
                {
                    close(result.fd);
                }
                else if(ret > 0)
                {
                    set_event_send(&ring,result.fd,buffer,ret,0);
                }
            
            }
            else if(result.event == EVENT_WRITE)
            {
                int ret = entries->res;
                printf("set_event_send ret:%d,buffer:%s\n",ret,buffer);
                set_event_recv(&ring,result.fd,buffer,BUFFER_LENGTH,0);
                
            
            }
        }
        
        io_uring_cq_advance(&ring,nready);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值