六、服务端开发

news2024/11/20 21:35:54

服务器端开发:

服务器设计框架:

缓解和转发的作用

连接音箱和app

最大一个作用转发

一个app操作音箱

app绑定音箱

服务器类实现:

jsoncpp

libvent

verser.h

#ifndef SERVER_H
#define SERVER_H

#include <event.h>


#define IP    "172.17.7.99"
#define PORT  8000


class PlayerServer
{
private:
    struct event_base *base;             //事件集合
    

    

public:
    PlayerServer(const char *ip = IP, int port = PORT);
    ~PlayerServer();


};


#endif

vim server.cpp

#include "server.h"
//初始化构造函数
PlayerServer::PlayerServer(const char *ip, int port)
{
    base = event_base_new();               //创建事件集合

    struct sockaddr_in server_addr; //服务器ip绑定
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip);
//使用libvent
    struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, 
        LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (NULL == listener)
    {
        std::cout << "evconnlistener_new_bind error" << std::endl;
    }

    event_base_dispatch(base);      //监听集合
}

PlayerServer::~PlayerServer()
{
    //释放两个对象
    evconnlistener_free(listener);
    event_base_free(base);
}

当有客户端发起连接的时候调用listener_cb回调函数

server.h

private:     //作为回调函数使用
    static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, 

server.cpp

void PlayerServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
    struct event_base *base = (struct event_base *)arg;//事件集合作为参数传入
    std::cout << "有客户端连接 " << fd << std::endl;

    //创建bufferevent事件,客户端连接一个bev,app连接一个bev base事件集合, fd客户端描述符
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (NULL == bev)
    {
        std::cout << "bufferevent_socket_new error" << std::endl;
    }

    bufferevent_setcb(bev, read_cb, NULL, event_cb, base);//调用两个回调函数,有读或者事件调用
    bufferevent_enable(bev, EV_READ);//使能bev
}

vim server.h

#ifndef SERVER_H
#define SERVER_H

#include <event.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string.h>
#include <event2/listener.h>
#include <jsoncpp/json/json.h>
#include <list>
#include <time.h>
#include "player.h"
#include "node.h"

#define IP    "172.17.7.99"
#define PORT  8000


class PlayerServer
{
private:
    struct event_base *base;             //事件集合
    struct evconnlistener *listener;

    static Player *p;
    static std::list<Node> *l;

public:
    PlayerServer(const char *ip = IP, int port = PORT);
    ~PlayerServer();

private:     //作为回调函数使用
    static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, 
                                                        int socklen, void *arg);     
    static void read_cb(struct bufferevent *bev, void *ctx);
    static void event_cb(struct bufferevent *bev, short what, void *ctx);
};


#endif

server.cpp

void PlayerServer::read_cb(struct bufferevent *bev, void *ctx)//当有客户端连接的可读时候调用该函数
{
    struct event_base *base = (struct event_base *)ctx;//事件集合作为参数传入
    char buf[1024] = {0};//接收数据
    size_t ret = bufferevent_read(bev, buf, sizeof(buf));//从客户端中读取数据
    if (ret < 0)
    {
        std::cout << "bufferevent_read error" << std::endl;
    }
}

void PlayerServer::event_cb(struct bufferevent *bev, short what, void *ctx)
{
    if (what & BEV_EVENT_EOF)
    {
       
                std::cout << "音箱下线" << std::endl;

    }
    else 
    {
        std::cout << "异常发生" << std::endl;
    }
}


在main.cpp创建服务器对象

#include "server.h"

int main()
{
    PlayerServer ps;

    return 0;
}

服务器数据解析:

这里解析

解析消息

有可能app发过来

有可能音箱发过来

app向服务器发送

音箱向服务器发送

server.h

server.cpp

void PlayerServer::read_cb(struct bufferevent *bev, void *ctx)
{
    struct event_base *base = (struct event_base *)ctx;//事件集合
    char buf[1024] = {0};//保存客户端数据
    size_t ret = bufferevent_read(bev, buf, sizeof(buf));//读取客户端数据
    if (ret < 0)
    {
        std::cout << "bufferevent_read error" << std::endl;
    }

    std::cout << buf << std::endl;

    Json::Reader reader; //解析json
    Json::Value val;

    bool result = reader.parse(buf, val);//解析val放到buf中
    if (!result)
    {
        std::cout << "解析json出错" << std::endl;
        return;
    }

    char cmd[32] = {0};//获取cmd键值
    strcpy(cmd, val["cmd"].asString().c_str());

    //app发送的消息
    if (!strcmp(cmd, "bind"))                //绑定功能
    {
        Node n;//结构体

        n.app_bev = bev;//保存appbev
        strcpy(n.app_id, val["appid"].asString().c_str());//获取appid
        strcpy(n.device_id, val["deviceid"].asString().c_str());//获取设备id
        n.online_flag = 0;//设备是否在线,设备不在线
        n.app_online_flag = 1;//app是否在线,app在线

        l->push_back(n);//把结构体放入链表中

        //回复APP
        val["cmd"] = "bind_success";//回复app连接成功
        //把Value对象转换成string
        std::string str = Json::FastWriter().write(val);
        size_t ret = bufferevent_write(bev, str.c_str(), strlen(str.c_str()));//回复给app
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }
    }
    else if (!strcmp(cmd, "search_bind"))     //查询是否已经绑定
    {
        //遍历链表,判断链表中是否存在 appid
        std::list<Node>::iterator it;
        for (it = l->begin(); it != l->end(); it++)
        {
            if (!strcmp(it->app_id, val["appid"].asString().c_str()))
            {
                it->app_bev = bev;           //更新 app_bev
                it->app_online_flag = 1;     //app在线

                //appid 存在
                val["cmd"] = "reply_bind";
                val["result"] = "yes";
                break;
            }
        }

        if (it == l->end())            //链表遍历到最后,appid 不存在
        {
            val["cmd"] = "reply_bind";
            val["result"] = "no";
        }

        std::string str = Json::FastWriter().write(val);
        size_t ret = bufferevent_write(bev, str.c_str(), strlen(str.c_str()));
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }

    }
    else if (!strcmp(cmd, "app_start"))      //开始播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_stop"))       //结束播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_suspend"))    //暂停播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_continue"))   //继续播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_prior"))      //上一首
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_next"))       //下一首
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_voice_up"))   //增加音量
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_voice_down")) //减小音量
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_sequence"))   //顺序播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_random"))     //随机播放
    {    
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_circle"))     //单曲播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_music"))      //获取音乐
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_off_line"))  //APP下线
    {
        for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
        {
            if (it->app_bev == bev)
            {
                it->app_online_flag = 0;
                bufferevent_free(it->app_bev);
                std::cout << "APP下线" << std::endl;
                break;
            }
        }
    }
    //音箱发送的消息
    else if (!strcmp(cmd, "reply"))          //操作成功回复
    {
        p->player_reply_result(l, bev, val);
    }
    else if (!strcmp(cmd, "info"))           //音箱的保活消息
    {
        p->player_alive_info(l, bev, val, base);    
    }
    else if (!strcmp(cmd, "reply_status"))   //回复播放器状态
    {
        p->player_reply_result(l, bev, val);
    }
    else if (!strcmp(cmd, "reply_music"))      //返回所有音乐
    {
        p->player_reply_result(l, bev, val);
    }
}

node.h

#ifndef NODE_H
#define NODE_H

#include <event.h>
#include <time.h>
#include <list>

struct Node 
{
    struct bufferevent *app_bev;
    struct bufferevent *device_bev;
    char app_id[8];
    char device_id[8];
    int online_flag;                     //表示设备是否在线  0 表示不在线
    int app_online_flag;                 //表示app 是否在线//
    time_t time;
    struct event timeout;                //定时器事件
};
typedef struct Node Node;

struct timeout_node
{
    std::list<Node> *l;
    char id[8];
};
typedef struct timeout_node tNode;

#endif

保活消息

vim player.h

vim player.cpp

判断device id在不在链表

player.h

#ifndef PLAYER_H
#define PLAYER_H

#include <list>
#include "node.h"
#include <event.h>
#include <jsoncpp/json/json.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <event2/util.h>
#include <stdlib.h>

class Player
{
private:

public:
    void player_alive_info(std::list<Node> *l, struct bufferevent *bev, Json::Value val, struct event_base *base);
    void player_operation(std::list<Node> *l, struct bufferevent *app_bev, const char *cmd);
    void player_reply_result(std::list<Node> *l, struct bufferevent *bev, Json::Value val);

    static void timeout_cb(evutil_socket_t fd, short event, void *arg);
};

#endif

player.cpp

#include "player.h"

void Player::player_alive_info(std::list<Node> *l, struct bufferevent *bev, Json::Value val, struct event_base *base)//保活调用
{
    char deviceid[8] = {0};//存放涉笔id
    strcpy(deviceid, val["deviceid"].asString().c_str());//把客户端的deviceid拷贝到deviceid中
    //从链表中查找deviceid
    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (!strcmp(deviceid, it->device_id))    //比较deviceid是否在链表中
        {
            if (it->online_flag == 0)           //表示设备不在线,第一次发送保活消息
            {
                tNode *t = new tNode;
                t->l = l;
                strcpy(t->id, deviceid);

                event_assign(&(it->timeout), base, -1, EV_PERSIST, timeout_cb, t);    //设置事件参数
                struct timeval tv;
                evutil_timerclear(&tv);
                tv.tv_sec = 1;                  //1秒执行一次
                event_add(&(it->timeout), &tv);

                it->online_flag = 1;            //表示设备在线
            }
            it->device_bev = bev;
            it->time = time(NULL);

            std::cout << "收到保活消息 信息更新成功" << std::endl;
            return;
        }
    }

    std::cout << "收到保活消息 但是设备未绑定" << std::endl;
}

服务器处理app消息:

app发送开始结束等消息

服务器收到这些消息转发

player.cpp

/*
描述:服务器收到APP消息,转发给音箱
参数:app_bev 对应的是app的bufferevent
*/
void Player::player_operation(std::list<Node> *l, struct bufferevent *app_bev, const char *cmd)
{
    Json::Value val;

    if (!strcmp(cmd, "app_start"))
    {
        val["cmd"] = "start";
    }
    else if (!strcmp(cmd, "app_stop"))
    {
        val["cmd"] = "stop";
    }
    else if (!strcmp(cmd, "app_suspend"))
    {
        val["cmd"] = "suspend";
    }
    else if (!strcmp(cmd, "app_continue"))
    {
        val["cmd"] = "continue";
    }
    else if (!strcmp(cmd, "app_prior"))
    {
        val["cmd"] = "prior";
    }
    else if (!strcmp(cmd, "app_next"))
    {
        val["cmd"] = "next";
    }
    else if (!strcmp(cmd, "app_voice_up"))
    {
        val["cmd"] = "voice_up";
    }
    else if (!strcmp(cmd, "app_voice_down"))
    {
        val["cmd"] = "voice_down";
    }
    else if (!strcmp(cmd, "app_sequence"))
    {
        val["cmd"] = "sequence";
    }
    else if (!strcmp(cmd, "app_random"))
    {
        val["cmd"] = "random";
    }
    else if (!strcmp(cmd, "app_circle"))
    {
        val["cmd"] = "circle";
    }
    else if (!strcmp(cmd, "app_music"))
    {
        val["cmd"] = "music";
    }

    std::string str = Json::FastWriter().write(val);
    size_t ret;
    
    //遍历链表判断是否在线
    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (it->app_bev == app_bev)    
        {
            if (it->online_flag == 1)      //在线
            {
                ret = bufferevent_write(it->device_bev, str.c_str(), strlen(str.c_str()));    
                if (ret < 0)
                {
                    std::cout << "bufferevent_write error" << std::endl;
                }
            }
            else
            {
                Json::Value v;
                v["cmd"] = "app_reply";
                v["result"] = "off_line";
                std::string s = Json::FastWriter().write(v);
                ret = bufferevent_write(app_bev, s.c_str(), strlen(s.c_str()));    
                if (ret < 0)
                {
                    std::cout << "bufferevent_write error" << std::endl;
                }

            }
        }
        break;
    }
}
/*
描述:收到音箱的回复消息,服务器回复APP
参数:bev 对应的是音箱的 bufferevent
*/
void Player::player_reply_result(std::list<Node> *l, struct bufferevent *bev, Json::Value val)
{    
    char cmd[32] = {0};
    strcpy(cmd, val["cmd"].asString().c_str());

    if (!strcmp(cmd, "reply"))
    {
        val["cmd"] = "app_reply";
    }
    else if (!strcmp(cmd, "reply_music"))
    {
        val["cmd"] = "app_reply_music";
    }
    else if (!strcmp(cmd, "reply_status"))
    {
        val["cmd"] = "app_reply_status";
    }

    std::string str = Json::FastWriter().write(val);
    size_t ret;
    //遍历链表,找到 app 对应的 bufferevent
    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (it->device_bev == bev)
        {
            if (it->app_online_flag == 1)
            {
                ret = bufferevent_write(it->app_bev, str.c_str(), strlen(str.c_str()));
                if (ret < 0)
                {
                    std::cout << "bufferevent_write" << std::endl;
                }
            }

            return;
        }
    }

    std::cout << "app 不存在" << std::endl;
}

void Player::timeout_cb(evutil_socket_t fd, short event, void *arg)
{
    std::cout << "定时器事件" << std::endl;
    tNode t;
    memcpy(&t, arg, sizeof(tNode));
    
    //1、根据 time 判断音箱是否在线
    std::list<Node>::iterator it;
    for (it = (t.l)->begin(); it != (t.l)->end(); it++)
    {
        if (!strcmp(it->device_id, t.id))
        {
            if (time(NULL) - it->time > 1)
            {
                it->online_flag = 0;     //表示不在线
            }
            else 
            {
                it->online_flag = 1;
            }
            break;
        }
    }

    //2、如果音箱和 APP 同时在线,则发起状态请求
    if (it->app_online_flag == 1 && it->online_flag == 1)
    {
        Json::Value val;
        val["cmd"] = "get";
        std::string str = Json::FastWriter().write(val);

        size_t ret = bufferevent_write(it->device_bev, str.c_str(), strlen(str.c_str()));
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }
    }
}

服务器定时任务:

服务器每隔一秒向音响播放播放器状态:

服务器开始定时器

libevent里面有一个定时器任务

思路

所有app 和音响对应关系在链表里面

每个节点都加一个定时器事件

修改一下链表,struct event timeout

定时器不一定启动,音响下线关掉

音响上线,向服务器发送保活消息,第一次发送启动一个定时器,online——flag

vim player.cpp

保活

先判断在不在线,第一次发送保活消息,然后启动定时器。

静态成员函数不能调用非静态成员变量

因为着急春招,写的比较潦草,主要是为了找工作对项目有一个印象,不是很详细,代码自取如下

一、main.cpp

#include "server.h"

int main()
{
    PlayerServer ps;

    return 0;
}

二、node.h

#ifndef NODE_H
#define NODE_H

#include <event.h>
#include <time.h>
#include <list>

struct Node 
{
    struct bufferevent *app_bev;
    struct bufferevent *device_bev;
    char app_id[8];
    char device_id[8];
    int online_flag;                     //表示设备是否在线  0 表示不在线
    int app_online_flag;                 //表示app 是否在线//
    time_t time;
    struct event timeout;                //定时器事件
};
typedef struct Node Node;

struct timeout_node
{
    std::list<Node> *l;
    char id[8];
};
typedef struct timeout_node tNode;

#endif

三、player.cpp

#include "player.h"

void Player::player_alive_info(std::list<Node> *l, struct bufferevent *bev, Json::Value val, struct event_base *base)
{
    char deviceid[8] = {0};
    strcpy(deviceid, val["deviceid"].asString().c_str());

    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (!strcmp(deviceid, it->device_id))    
        {
            if (it->online_flag == 0)           //表示设备不在线,第一次发送保活消息
            {
                tNode *t = new tNode;
                t->l = l;
                strcpy(t->id, deviceid);

                event_assign(&(it->timeout), base, -1, EV_PERSIST, timeout_cb, t);    //设置事件参数
                struct timeval tv;
                evutil_timerclear(&tv);
                tv.tv_sec = 1;                  //1秒执行一次
                event_add(&(it->timeout), &tv);

                it->online_flag = 1;            //表示设备在线
            }
            it->device_bev = bev;
            it->time = time(NULL);

            std::cout << "收到保活消息 信息更新成功" << std::endl;
            return;
        }
    }

    std::cout << "收到保活消息 但是设备未绑定" << std::endl;
}

/*
描述:服务器收到APP消息,转发给音箱
参数:app_bev 对应的是app的bufferevent
*/
void Player::player_operation(std::list<Node> *l, struct bufferevent *app_bev, const char *cmd)
{
    Json::Value val;

    if (!strcmp(cmd, "app_start"))
    {
        val["cmd"] = "start";
    }
    else if (!strcmp(cmd, "app_stop"))
    {
        val["cmd"] = "stop";
    }
    else if (!strcmp(cmd, "app_suspend"))
    {
        val["cmd"] = "suspend";
    }
    else if (!strcmp(cmd, "app_continue"))
    {
        val["cmd"] = "continue";
    }
    else if (!strcmp(cmd, "app_prior"))
    {
        val["cmd"] = "prior";
    }
    else if (!strcmp(cmd, "app_next"))
    {
        val["cmd"] = "next";
    }
    else if (!strcmp(cmd, "app_voice_up"))
    {
        val["cmd"] = "voice_up";
    }
    else if (!strcmp(cmd, "app_voice_down"))
    {
        val["cmd"] = "voice_down";
    }
    else if (!strcmp(cmd, "app_sequence"))
    {
        val["cmd"] = "sequence";
    }
    else if (!strcmp(cmd, "app_random"))
    {
        val["cmd"] = "random";
    }
    else if (!strcmp(cmd, "app_circle"))
    {
        val["cmd"] = "circle";
    }
    else if (!strcmp(cmd, "app_music"))
    {
        val["cmd"] = "music";
    }

    std::string str = Json::FastWriter().write(val);
    size_t ret;
    
    //遍历链表判断是否在线
    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (it->app_bev == app_bev)    
        {
            if (it->online_flag == 1)      //在线
            {
                ret = bufferevent_write(it->device_bev, str.c_str(), strlen(str.c_str()));    
                if (ret < 0)
                {
                    std::cout << "bufferevent_write error" << std::endl;
                }
            }
            else
            {
                Json::Value v;
                v["cmd"] = "app_reply";
                v["result"] = "off_line";
                std::string s = Json::FastWriter().write(v);
                ret = bufferevent_write(app_bev, s.c_str(), strlen(s.c_str()));    
                if (ret < 0)
                {
                    std::cout << "bufferevent_write error" << std::endl;
                }

            }
        }
        break;
    }
}

/*
描述:收到音箱的回复消息,服务器回复APP
参数:bev 对应的是音箱的 bufferevent
*/
void Player::player_reply_result(std::list<Node> *l, struct bufferevent *bev, Json::Value val)
{    
    char cmd[32] = {0};
    strcpy(cmd, val["cmd"].asString().c_str());

    if (!strcmp(cmd, "reply"))
    {
        val["cmd"] = "app_reply";
    }
    else if (!strcmp(cmd, "reply_music"))
    {
        val["cmd"] = "app_reply_music";
    }
    else if (!strcmp(cmd, "reply_status"))
    {
        val["cmd"] = "app_reply_status";
    }

    std::string str = Json::FastWriter().write(val);
    size_t ret;
    //遍历链表,找到 app 对应的 bufferevent
    for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
    {
        if (it->device_bev == bev)
        {
            if (it->app_online_flag == 1)
            {
                ret = bufferevent_write(it->app_bev, str.c_str(), strlen(str.c_str()));
                if (ret < 0)
                {
                    std::cout << "bufferevent_write" << std::endl;
                }
            }

            return;
        }
    }

    std::cout << "app 不存在" << std::endl;
}

void Player::timeout_cb(evutil_socket_t fd, short event, void *arg)
{
    std::cout << "定时器事件" << std::endl;
    tNode t;
    memcpy(&t, arg, sizeof(tNode));
    
    //1、根据 time 判断音箱是否在线
    std::list<Node>::iterator it;
    for (it = (t.l)->begin(); it != (t.l)->end(); it++)
    {
        if (!strcmp(it->device_id, t.id))
        {
            if (time(NULL) - it->time > 1)
            {
                it->online_flag = 0;     //表示不在线
            }
            else 
            {
                it->online_flag = 1;
            }
            break;
        }
    }

    //2、如果音箱和 APP 同时在线,则发起状态请求
    if (it->app_online_flag == 1 && it->online_flag == 1)
    {
        Json::Value val;
        val["cmd"] = "get";
        std::string str = Json::FastWriter().write(val);

        size_t ret = bufferevent_write(it->device_bev, str.c_str(), strlen(str.c_str()));
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }
    }
}

四、player.h

#ifndef PLAYER_H
#define PLAYER_H

#include <list>
#include "node.h"
#include <event.h>
#include <jsoncpp/json/json.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <event2/util.h>
#include <stdlib.h>

class Player
{
private:

public:
    void player_alive_info(std::list<Node> *l, struct bufferevent *bev, Json::Value val, struct event_base *base);
    void player_operation(std::list<Node> *l, struct bufferevent *app_bev, const char *cmd);
    void player_reply_result(std::list<Node> *l, struct bufferevent *bev, Json::Value val);

    static void timeout_cb(evutil_socket_t fd, short event, void *arg);
};

#endif

五、server.cpp

#include "server.h"

Player *PlayerServer::p = new Player();      //创建播放器对象
std::list<Node> *PlayerServer::l = new std::list<Node>();

PlayerServer::PlayerServer(const char *ip, int port)
{
    base = event_base_new();               //创建事件集合

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip);

    listener = evconnlistener_new_bind(base, listener_cb, base, 
        LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (NULL == listener)
    {
        std::cout << "evconnlistener_new_bind error" << std::endl;
    }

    event_base_dispatch(base);      //监听集合
}

PlayerServer::~PlayerServer()
{
    //释放两个对象
    evconnlistener_free(listener);
    event_base_free(base);
}

void PlayerServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
    struct event_base *base = (struct event_base *)arg;
    std::cout << "有客户端连接 " << fd << std::endl;

    //创建bufferevent事件
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (NULL == bev)
    {
        std::cout << "bufferevent_socket_new error" << std::endl;
    }

    bufferevent_setcb(bev, read_cb, NULL, event_cb, base);
    bufferevent_enable(bev, EV_READ);
}

void PlayerServer::read_cb(struct bufferevent *bev, void *ctx)
{
    struct event_base *base = (struct event_base *)ctx;
    char buf[1024] = {0};
    size_t ret = bufferevent_read(bev, buf, sizeof(buf));
    if (ret < 0)
    {
        std::cout << "bufferevent_read error" << std::endl;
    }

    std::cout << buf << std::endl;

    Json::Reader reader;
    Json::Value val;

    bool result = reader.parse(buf, val);
    if (!result)
    {
        std::cout << "解析json出错" << std::endl;
        return;
    }

    char cmd[32] = {0};
    strcpy(cmd, val["cmd"].asString().c_str());

    //app发送的消息
    if (!strcmp(cmd, "bind"))                //绑定功能
    {
        Node n;

        n.app_bev = bev;
        strcpy(n.app_id, val["appid"].asString().c_str());
        strcpy(n.device_id, val["deviceid"].asString().c_str());
        n.online_flag = 0;
        n.app_online_flag = 1;

        l->push_back(n);

        //回复APP
        val["cmd"] = "bind_success";
        //把Value对象转换成string
        std::string str = Json::FastWriter().write(val);
        size_t ret = bufferevent_write(bev, str.c_str(), strlen(str.c_str()));
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }
    }
    else if (!strcmp(cmd, "search_bind"))     //查询是否已经绑定
    {
        //遍历链表,判断链表中是否存在 appid
        std::list<Node>::iterator it;
        for (it = l->begin(); it != l->end(); it++)
        {
            if (!strcmp(it->app_id, val["appid"].asString().c_str()))
            {
                it->app_bev = bev;           //更新 app_bev
                it->app_online_flag = 1;     //app在线

                //appid 存在
                val["cmd"] = "reply_bind";
                val["result"] = "yes";
                break;
            }
        }

        if (it == l->end())            //链表遍历到最后,appid 不存在
        {
            val["cmd"] = "reply_bind";
            val["result"] = "no";
        }

        std::string str = Json::FastWriter().write(val);
        size_t ret = bufferevent_write(bev, str.c_str(), strlen(str.c_str()));
        if (ret < 0)
        {
            std::cout << "bufferevent_write error" << std::endl;
        }

    }
    else if (!strcmp(cmd, "app_start"))      //开始播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_stop"))       //结束播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_suspend"))    //暂停播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_continue"))   //继续播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_prior"))      //上一首
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_next"))       //下一首
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_voice_up"))   //增加音量
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_voice_down")) //减小音量
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_sequence"))   //顺序播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_random"))     //随机播放
    {    
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_circle"))     //单曲播放
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_music"))      //获取音乐
    {
        p->player_operation(l, bev, cmd);
    }
    else if (!strcmp(cmd, "app_off_line"))  //APP下线
    {
        for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
        {
            if (it->app_bev == bev)
            {
                it->app_online_flag = 0;
                bufferevent_free(it->app_bev);
                std::cout << "APP下线" << std::endl;
                break;
            }
        }
    }
    //音箱发送的消息
    else if (!strcmp(cmd, "reply"))          //操作成功回复
    {
        p->player_reply_result(l, bev, val);
    }
    else if (!strcmp(cmd, "info"))           //音箱的保活消息
    {
        p->player_alive_info(l, bev, val, base);    
    }
    else if (!strcmp(cmd, "reply_status"))   //回复播放器状态
    {
        p->player_reply_result(l, bev, val);
    }
    else if (!strcmp(cmd, "reply_music"))      //返回所有音乐
    {
        p->player_reply_result(l, bev, val);
    }
}

void PlayerServer::event_cb(struct bufferevent *bev, short what, void *ctx)
{
    if (what & BEV_EVENT_EOF)
    {
        for (std::list<Node>::iterator it = l->begin(); it != l->end(); it++)
        {
            if (it->device_bev == bev)
            {
                std::cout << "音箱下线" << std::endl;
                it->online_flag = 0;              //表示不在线
                event_del(&it->timeout);           //移除定时器
                return;
            }
            /*if (it->app_bev == bev)
            {
                std::cout << "APP 下线" << std::endl;
                it->app_online_flag = 0;
            }*/
        }
    }
    else 
    {
        std::cout << "异常发生" << std::endl;
    }
}


六、server.h

#ifndef SERVER_H
#define SERVER_H

#include <event.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string.h>
#include <event2/listener.h>
#include <jsoncpp/json/json.h>
#include <list>
#include <time.h>
#include "player.h"
#include "node.h"

#define IP    "172.17.7.99"
#define PORT  8000


class PlayerServer
{
private:
    struct event_base *base;             //事件集合
    struct evconnlistener *listener;

    static Player *p;
    static std::list<Node> *l;

public:
    PlayerServer(const char *ip = IP, int port = PORT);
    ~PlayerServer();

private:     //作为回调函数使用
    static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, 
                                                        int socklen, void *arg);     
    static void read_cb(struct bufferevent *bev, void *ctx);
    static void event_cb(struct bufferevent *bev, short what, void *ctx);
};


#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++入门】缺省参数

目  录1 缺省参数1.1 缺省参数概念1.2 缺省参数分类1.3 缺省参数使用注意1 缺省参数 1.1 缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。 在调用该函数时&#xff0c;如果没有指定实参&#xff0c;则采用该形参的缺省值&#xff0c;否则使用指定的实…

Spring Profiles 实现多环境配置 ,切换环境

Spring Profiles 实现多环境配置 Spring Profiles 就是针对应用程序&#xff0c;不同环境需要不同配置加载的一种解决方案。 使用场景: 我们平常项目开发&#xff0c;经常需要根据不同的环境进行配置的修改&#xff0c;比如在本地开发会加载本机的配置和开发环境数据库&#x…

【NKOJ-昨天今天和明天】考试游记

目录 昨天上午的序列 - 30pts今天上午的扫除 - 100pts明天上午的教室 - 100pts明天上午的数组 - 100pts明天上午的函数 - 100pts 题目排序太合理了,EDCBA依次变难,导致我对着A题苦思冥想了半小时...但是A题是[LeetCode 907. 子数组的最小值之和]的换皮题(除了题目一点没变)然…

初始OAuth2.0

1. 什么是OAuth2.0 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用于授权第三方应用获取用户的数据。 举例说明&#xff1a;用户可以通过选择其他登录方式来使用gitee&#xff0c;这里就使用到了第三方认证。 OAuth 引入了一个授权层&#xff0c;用来分离两种不同的角色…

前端工程化

一、前端工程化1、webpack&#xff08;1&#xff09;定义&#xff1a;是一个前端的构建工具。前端代码格式多&#xff08;html、css、js、图片&#xff09;&#xff0c;前端构建工具的作用是将各种格式不同文件打包到一起&#xff0c;方便项目的上线运行。&#xff08;将开发环…

Java学习笔记---干货

Java学习 一、java版本 JavaSE &#xff1a;标准版&#xff08;桌面应用程序、控制台程序&#xff09; JavaEE&#xff1a;E企业级开发&#xff08;Web端&#xff0c;服务器的开发&#xff09; JDK&#xff1a;Java开发工具 JRE&#xff1a;Java运行环境 JVM&#xff1a;…

通信原理笔记—基带信号的功率谱

目录 二进制纯随机序列基带信号的功率谱&#xff1a; 基带信号的功率谱分析&#xff1a; (1)信号(t)的功率谱&#xff1a; (2)信号v(t)的功率谱&#xff1a; 随机序列S(t)的功率谱&#xff1a; 二进制纯随机序列基带信号的功率谱&#xff1a; 基带信号的功率谱分析&#…

软件需求说明书确保正确性的6大关键点

软件需求说明书对软件开发项目非常关键&#xff0c;如何确保其正确性&#xff0c;关键有6大要点&#xff01; 1、需求与其他需求是否相互冲突或重复 一般需求规格说明书长达几百页&#xff0c;并不是一蹴而就的&#xff0c;因此可能出现前后观点的重叠或差异&#xff0c;或同一…

[HDCTF2019]Maze 题解

少欲则心静&#xff0c;心静则事简。 ——人民日报 1.查壳 是一个加了upx壳的32位EXE文件 2.使用Kali Linux脱壳 maze题目脱壳3.去除脏字节 没有找到主函数&#xff0c;发现这段汇编代码标红了&#xff0c;IDA分析崩溃&#xff0c;这是掺杂了花指令 这里jnz&#xff0c;不论判…

中国电子学会2022年03月份青少年软件编程Scratch图形化等级考试试卷三级真题(含答案)

青少年软件编程&#xff08;图形化&#xff09;等级考试试卷&#xff08;三级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 一、单选题(共25题&#xff0c;共50分) 1. 以下四个选项中&#xff0c;运行哪个积木块&#xff0c;可能得到523这个数值&#xff1f;&…

25-35分布式事务seata

分布式事务Seata使用及其原理剖析 Seata的三大角色 TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器 定义全局事务的范围&#xff1a;开始全局事务、提交或回滚全局事务。 RM…

devicetree和启动参数解析流程

devicetree和启动参数解析流程 文章目录devicetree和启动参数解析流程一、设备树解析概述二、early device tree 解析流程三、device node节点创建流程四、bootargs参数解析&#xff14;.1 bootargs参数配置&#xff14;.&#xff12; early param参数解析&#xff14;.&…

Docker - 2. Docker 工作原理

目录 1. Docker入门图解 2. Docker 整体架构和底层通讯原理 1. Docker入门图解 (1) Docker是一个Client-Server结构的系统&#xff0c;Docker守护进程(图中Docjer daemon)运行在主机上&#xff0c;然后通过Socket链接客户端往返&#xff0c;守护进程从客户端接收命令并管理在…

PySpark 之 SparkSQL 编程

1. DataFrame 的创建 1.1 RDD 和 DataFrame 的区别 RDD 是一种弹性分布式数据集&#xff0c;Spark中的基本抽象。表示一种不可变的、分区储存的集合&#xff0c;可以进行并行操作DataFrame是一种以列对数据进行分组表达的分布式集合&#xff0c; DataFrame等同于Spark SQL中的…

jvm宏观上类的加载机制整体和微观上通过类加载器进行加载的过程

说到一个词“类的加载”其实含有歧义&#xff0c;因为在jvm中可以说有一个宏观的&#xff0c;即整体上的类的加载&#xff0c;还有一个微观上的加载&#xff0c;也就是狭隘的通过类加载器的加载class文件的过程&#xff0c;这里介绍这两种“类的加载”。 类的整体加载过程(类加…

Windows C盘清理的正确方式,从此你告别红色烦恼

前言 伴随着电脑工作的时间越久&#xff0c;C盘常常会提示显示其内存已不足。 C盘容量不足将会极大影响系统的运行速度&#xff0c;电脑会变卡、死机。 今天&#xff0c;就给大家分享一个C盘空间清理终极解决方案&#xff1a; 1、利用Windows自己附带的磁盘清理工具 1&…

[oeasy]python0068_ 字体样式_正常_加亮_变暗_控制序列

字体样式 回忆上次内容 上次了解了一个新的转义模式 \33 逃逸控制字符 esc esc 让输出 退出标准输出流 进行控制信息的设置 可以清屏也可以设置光标输出的位置 还能做什么呢&#xff1f; 可以设置字符的颜色吗&#xff1f;&#xff1f;&#xff1f;&#x1f914; 查看细节…

BIC-2022-BDT:区块链和基于数字双胞胎的智能制造高效数据处理安全框架

摘要工业物联网具有智能连接、数据实时处理、协同监测、信息自动处理等特点&#xff0c;是物联网时代的重要组成部分之一。异构工业物联网设备对高数据速率、高可靠性、高覆盖、低延迟的要求&#xff0c;已成为信息安全领域的一大挑战。工业物联网中的智能制造产业需要多方协同…

(02)Cartographer源码无死角解析-(53) 2D后端优化→位姿图优化理论讲解、

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文末…

【ardunio+sx1268】与【esp32+sx1268】实现不同主控单片机lora通讯

2023.21 在前文 esp32 sx1268 的 spi 驱动调通之后&#xff0c;又尝试 ardunio sx1268 驱动&#xff0c;实现不同主控对于lora模块 sx1268 的控制 文章目录1. 实验结果2.硬件描述2.1 sx12682.2 ardunio ATmega3283.接线实物图5.开发环境6.代码实现关于esp32sx1268 的驱动以及代…