Day7:Lars V0.9 UDP协议支持 +Lars V0.10 异步消息任务机制

1 Lars V0.9 UDP协议支持

1.1单线程udp_server类

1.1.1属性

        (1) _read_buf/_write_buf:读写缓存

        (2) event_loop

        (3) 临时记录客户端的地址

        (4) 消息路由分发机制

event_loop *_loop;
char _read_buf[MESSAGE_LENGTH_LIMIT];
char _write_buf[MESSAGE_LENGTH_LIMIT];

// 客户端IP地址
struct sockaddr_in _client_addr;
socklen_t _client_addrlen;

//消息路由分发机制
msg_router _router;

1.1.2方法

        (1)构造函数,创建服务

udp_server::udp_server(event_loop *loop, const char *ip, uint16_t port)
{
     //0.忽略一些信号 SIGHUB, SIGPIPE
    if(singal(SIGHUP, SIG_IGN)==SIG_ERR){
        fprintf(stderr, "signal ingore SIGHUB\n");
    }
    //1.创建socket
    _sockfd = socket(AF_INET, SOCK_DGRAM|SOCKNONBLOCK|SOCK_CLOEXEC, IPPROTO_UDP);
    if(_sockfd==-1)
    {
        perror("create udp server");
        exit(1);
    }

    //2.初始化服务器地址
    struct sockaddr_in server_addr;
    ZeroMemory(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    inet_pton(ip, &server_addr.sin_addr);
    server_addr.sin_port = htons(port);

    //3.绑定端口
    if(bind(_sockfd, (const struct sockaddr*)&server_addr, sizeof(server_addr))<0)
    {
        fprintf(stderr, "bind error\n");
        exit(1);
    }

    // 4.添加loop事件监控
    this->_loop = loop;
    

    // 5.清空客户端地址
    ZeroMemory(& _client_addr, sizeof(_client_addr));
    _client_addrlen = sizeof(_client_addr);

    // 监控_sockfd读事件,如果刻度,代表客户端有数据
    loop->add_io_envent(_sockfd, read_callback, EPOLLIN, this);

}


// 服务端处理客户端的读事件
void read_callback(event_loop *loop, int fd, void *args)
{
    udp_server *server = (udp_server*)args;
    server->do_read();
}

// 读客户端数据
void udp_server::do_read()
{
    while(true)
    {
        int pkg_len = recvfrom(_sockfd, _read_buf, sizeof(_read_buf), 0, (sockaddr*)&_client_addr, &_client_addrlen);
        if(pkg_len==-1)
        {
            // 中断错误
            continue;
        }
        else if(errno==EAGAIN)
        {
            // 非阻塞
            break;
        }
        else{
            perror("udp server recv from error");
            break;}

        msg_head head;
        memcpy(&head, _read_buf, MESSAGE_HEAD_LEN);
        if(head.msglen>MESSAGE_LENGTH_LIMIT||head.msglen<0||head.msglen+MESSAGE_HEAD_LEN!=pkg_len)
        {
            fprintf(stderr, "error message head format error\n");
            continue;
        }

        _router.call(head.msgid, head.msgid, _read_buf+MESSAGE_HEAD_LEN, this);
    }
}

        (2)添加消息路由

// 注册一个msgid和回调业务的路由关系
void udp_server::add_msg_router(int msgid, msg_callback *cb, void *user_data)
{
    _router.registered_msg_router(msgid, cb, user_data);
}

udp_server::~udp_server()
{
    _loop->delete_io_event(_sockfd);
    close(_sockfd);
}

        (3)主动发包

int udp_server::send_message(const char *data, int msglen, int msgid)
{
    // udpserver主动发包给客户端
    if(msglen>MESSAGE_LENGTH_LIMIT)
    {
        fprintf(stderr, "message too big\n");
        return -1;
    }

    msg_head head;
    head.msglen = msglen;
    head.msgid = msgid;
    // 将head写到输出缓冲中
    memcpy(_write_buf, &head, MESSAGE_HEAD_LEN);
    // 将data写到输出缓冲中
    memcpy(_write_buf+MESSAGE_HEAD_LEN, data, msglen);
    // 通过_client_addr将报文发送给对方客户端
    int ret = sendto(_sockfd, _write_buf, msglen+MESSAGE_HEAD_LEN, 0, (const sockaddr*)&_client_addr, _client_addrlen);
    return ret;
}

1.2udp_client类

        udp_client类与server类似

#include<udp_client.h>
#include<message.h>
#include<io.h>

udp_client::udp_client(event_loop *loop, const char *ip, uint16_t port)
{
    // 创建套接字
    _sockfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, IPPROTO_UDP);
    if(_sockfd==-1)
    {
        perror("create udp server");
        exit(1);
    }

    // 2 初始化要连接服务器的地址
    struct sockaddr_in server_addr;
    ZeroMemory(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    inet_aton(ip, &server_addr.sin_addr);

    // 连接服务器
    int ret = connect(_sockfd, (const sockaddr*)&server_addr, sizeof(server_addr));
    if(ret==-1)
    {
        perror("connect");
        exit(1);
    }

    // 3 添加一个读事件
    this->_loop = loop;
    _loop->add_io_envent(_sockfd, read_callback, EPOLLIN, this);



}
udp_client::~udp_client()
{
    _loop->delete_io_event(_sockfd);
    closesocket(_sockfd);
}


void udp_client::add_msg_router(int msgid, msg_callback *cb, void *user_data)
{
    _router.registered_msg_router(msgid, cb, user_data);
}


int udp_client::send_message(const char *data, int msglen, int msgid)
{
    if(msglen>MESSAGE_LENGTH_LIMIT)
    {
        fprintf(stderr, "message too big\n");
        return -1;
    }

    msg_head head;
    head.msglen = msglen;
    head.msgid = msgid;
    // 将head写到输出缓冲中
    memcpy(_write_buf, &head, MESSAGE_HEAD_LEN);
    // 将data写到输出缓冲中
    memcpy(_write_buf+MESSAGE_HEAD_LEN, data, msglen);
    // 通过_client_addr将报文发送给对方客户端
    int ret = sendto(_sockfd, _write_buf, msglen+MESSAGE_HEAD_LEN, 0, NULL, NULL);
    return ret;
}


void udp_client::do_read()
{
    while(true)
    {
        int pkg_len = recvfrom(_sockfd, _read_buf, sizeof(_read_buf), 0, NULL, 0);
        if(pkg_len==-1)
        {
            // 中断错误
            continue;
        }
        else if(errno==EAGAIN)
        {
            // 非阻塞
            break;
        }
        else{
            perror("udp client recv from error");
            break;}

        msg_head head;
        memcpy(&head, _read_buf, MESSAGE_HEAD_LEN);
        if(head.msglen>MESSAGE_LENGTH_LIMIT||head.msglen<0||head.msglen+MESSAGE_HEAD_LEN!=pkg_len)
        {
            fprintf(stderr, "error message head format error\n");
            continue;
        }

        _router.call(head.msgid, head.msgid, _read_buf+MESSAGE_HEAD_LEN, this);
    }
}


void read_callback(event_loop *loop, int fd, void *args)
{
    udp_client *client = (udp_client *)args;
    // 处理读业务
    client->do_read();
}

2 Lars V0.10 异步消息任务机制

        异步消息指非阻塞消息,异步任务的执行时机在event_loop一轮处理读写事件之后。

2.1一般任务

2.1.1属性

        当前全部已经就绪的task任务集合

typedef std::pair<task_func, void *> task_func_pair;
// 目前已经就绪的task任务
std::vector<task_func_pair> _ready_task;

2.1.2方法

        (1)添加一个task到集合中

void event_loop::add_task(task_func func, void *args)
{
    task_func_pair func_pair(func, args);
    _ready_task.push_back(func_pair);
}

        (2)执行全部task的方法

void event_loop::execute_ready_tasks()
{
    std::vector<task_func_pair>::it;
    for(it=_ready_task.begin(); it!=_ready_task.end(); it++)
    {
        task_func func = it->first;
        void *args = it->second;

        // 调用任务函数
        func(this, args);
    }

    // 全部执行完毕,清空当前的_ready_task集合
    _ready_task.clear();
}

2.2对于新建立的连接

        给thread_pool提供一个NEW_TASK接口,使tcp_server能够获取thread_pool的对象。

void thread_pool::send_task(task_func func, void *args)
{
    // 给当前thread_pool中每一个thread里的queue去发送当前任务
    task_msg task;
    task.type = task_msg::NEW_TASK;
    task.task_cb = func;
    task.args = args;

    for(int i=0; i<_thread_cnt; i++)
    {
        // 取出第i个现成的消息队列queue
        thread_queue<task_msg> *queue = _queues[i];
        // 给queue发送一个task
        queue->send(task);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值