高并发异步非阻塞程序消息发送处理机制

/*
在网络IO密集型的高并发异步非阻塞程序里,网络的IO吞吐量一般远远跟不上CPU的处理能力,
程序在发送大量数据时,会导致TCP的发送缓冲区满,应用层在调用send或write等函数时会返回 EAGAIN(重试)。
由于程序是异步非阻塞的,需要把没有发送完的数据保存起来,等TCP发送缓冲区有空闲空间时重新触把保存的数据写到发送缓冲区里。
这里我们介绍一个把没有发送完的数据保存到队列里,并在发送缓冲区空闲时或定时器定时的触发的相关处理函数
*/

//存储任务消息的队列

std::queue<STTask> task_queue;

//网络套接字
int net_fd = -1;



//保存数据的结构体
struct STTask
{
    unsigned int snd;
    std::string  data;
    STTask()
    {
        snd = 0;
        data.clear();
    }
    
    /*
    描述:创建消息的带参构造函数
    参数:
        msg:     消息指针
        msg_len: 消息的长度
        snd_len: 已发送消息的长度
    */

    STTask(const char* msg, int msg_len, int snd_len):
    data(msg,msg_len),
snd(snd_len)
    {
    }


void Reset()
{
   snd = 0;
}
};



/*
描述:创建消息的带参构造函数
参数:
    fd:   套接字描述符
    buff: 缓冲区指针
    len:  消息的长度
*/

int sendToTcp(int fd, char* buff, const int len)
{
    int ret;
    int _snd=0;
    int _len = len;
    errno = 0;


    while(_len > 0)
    {
        ret = send(fd, (void*)((char*)buff + _snd), _len, 0);
        if ( ret <= 0 )
        {
            if ( errno == EINTR || errno == EAGAIN )
            {
                break;
            }
            else
            {
                printf("ret:%d, errno:%d, desc:%s\n", ret, errno, strerror(errno));
                return -1;
            }
        }


        _len -= ret;
        _snd += ret;
    }


    return _snd;
}


/*
描述:重新激活发送数据(缓冲区有可写事件可调用此函数发送未发送的队列任务消息)
参数:
    无
*/

int ReactivateSend()
{
    if(task_queue.empty())
    {
        //没有数据可发送, 删除写事件(也就是只设置可读、关闭、错误等事件)
        //注:设置事件

        return 0;
    }


    int count = sendQueueTask();


    if(task_queue.empty())
    {
        //没有数据可发送, 删除写事件(也就是只设置可读、关闭、错误等事件)
        //注:设置事件

        return 0;
    }


    return 0;
}



/*
    描述:循环发送队列里的任务数据
    参数:
        无
*/

int sendQueueTask()
{
    int task_len = 0;
    int task_num = 0;
    while(!task_queue.empty())
    {
        //取出队列头的消息,这里要保证顺序
        STTask & task = task_queue.front();


        if(task.snd > 0)
        {
            task_len = task.data.size() - task.snd;
        }
        else
        {
            task_len = task.data.size();
        }


        //注:net_fd在这之前需要先调用socket(), connect()等函数初始化
        int send_len = sendToTcp(net_fd, (char*)task.data.c_str() + task.snd, task_len);
        if(send_len < 0 )
        {
            //发送错误关闭缓冲区
            //注:关闭套接字描述符

            return task_num ;
        }


        if(send_len != task_len)
        {
            //消息没发送完成, 消息不取出来,只设置已发送的长度并打开写事件
            //注:设置事件(设置可写、可读、关闭、错误等事件)

            task_queue.front().snd += send_len;
            return task_num;
        }


        ++task_num;
        task_queue.pop();
    }


    return task_num;
}



/*
描述:创建消息的带参构造函数
参数:
    msg:     消息指针
    msg_len: 消息的长度
    snd_len: 已发送消息的长度
*/

int createTask(const char* msg, int msg_len, int snd)
{
    STTask task(msg, msg_len, snd);


    task_queue.push(task);


    return 0;
}



/*
描述:发送消息函数(所有的消息重这里发送)
参数:
    buff: 消息缓冲区指针
    len:  消息的长度
*/

int sendMessage(char* buff, int len)
{
    if(buff == 0 || len <=0)
    {
        return -1 ;
    }


    int snd = 0;
    //任务队列如果为空,先偿试一下是否能发送出来
    if(task_queue.empty())
    {
        snd = sendToTcp(net_fd, buff, len);
        if( snd < 0 )
        {
            //发生错误,不用构造,(这里如果消息不能丢,就需要保存)
            //注:关闭套接字描述符

            return -1;
        }


        if(snd != len)
        {
            //消息没发送完成, 创建消息任务并打开写事件
            createTask(buff, len, snd);
            //注:设置事件(设置可写、可读、关闭、错误等事件)
        }
    }
    else
    {
        //任务队列不为空,需要放在队列里,这里为了保证消息的顺序
        createTask(buff, len);
        snd = len;
    }


    return snd;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值