libevent TCP服务器主动发送消息给客户端

最近要做一个关于libevent性能测试的任务,在琢磨如何在TCP服务器上主动发送消息给客户端的时候弄了很久。写篇博客记录一下。

网上的例子大多都是,先建立一个监听回调,监听到之后新建一个bufferevent,然后在bufferevent上建立读写回调,在读回调中对消息进行显示(或者其它处理),然后调用bufferevent_write函数,发送回复信息。

这样的做法,一个非常致命的缺点是,服务器只能进行响应式发送消息,客户端没发送消息的时候,服务器是无法向客户端发送响应信息的。但是有些时候服务器需要主动向客户端发消息,怎么办?

我在网上查了挺久发现没什么有用的信息,在https://bbs.csdn.net/topics/392035999?page=1这里看到有人问了和我一样的问题,有人回答说用bufferevent_write函数。

我想,既然主线程在event_base_dispatch函数阻塞了,那我能不能开一个新线程,在新线程里面调用bufferevent_write,达到主动发送的效果呢!

于是立马动手试了一下,发现好像没用。。只有在接收到信息之后,我想主动发送的消息才会发出去。

我想起libevent要支持多线程好像必须要加个啥函数来着,然后加上去了,成功了!nice啊。不知道libevent的http能不能用同样的方法,让服务器主动发消息。

附上服务器代码(windows版本,linux下只需要小改就行了)

ps:代码是在另一位博主写的服务器代码上改的,附上原文链接:https://blog.csdn.net/woniu211111/article/details/84315998


#include 
#include 
#include 
#include 
#include 
#include 

#ifdef _WIN32
#include 
#include 
#include 
#else
#include 
#include 
#endif

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
#pragma comment(lib, "libevent_extras.lib")

using namespace std;


DWORD WINAPI SendThread(void *arg){
    struct bufferevent *bev = (struct bufferevent *)arg;
    char *p = "我是服务器,我在主动向你发送数据!" ;
    while(1){
        //写数据给客户端
        Sleep(1000);
        if(bev)    bufferevent_write(bev, p, strlen(p)+1);
    }
}


void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0};

    bufferevent_read(bev, buf, sizeof(buf));
    cout << "client say: " << buf << endl;
}


/************************************
@ Brief:        写缓冲区回调
************************************/
void write_cb(struct bufferevent *bev, void *arg)
{
    cout << "I'm 服务器,成功写数据给客户端,写缓冲回调函数被调用..." << endl;
}

/************************************
@ Brief:        事件回调
************************************/
void event_cb(struct bufferevent *bev, short events, void *arg)
{
    char* ip = (char*)arg;
    if (events & BEV_EVENT_EOF)
    {
        cout << "connection closed:" << ip << endl;
    }
    else if (events & BEV_EVENT_ERROR)
    {
        cout << "some other error !" << endl;
    }

    bufferevent_free(bev);
    bev = NULL;
    cout << "bufferevent 资源已经被释放..." << endl;
}

/************************************
@ Brief:        监听回调
************************************/
void cb_listener(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int len, void *ptr)
{
    struct sockaddr_in* client = (sockaddr_in*)addr ;
    cout << "connect new client: " << inet_ntoa(client->sin_addr) << "::"<< ntohs(client->sin_port)<< endl;

    struct event_base *base = (struct event_base*)ptr;

    //添加新事件
    struct bufferevent *bev;
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

    //给bufferevent缓冲区设置回调
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, inet_ntoa(client->sin_addr));

    //启动 bufferevent的 读缓冲区。默认是disable 的
    bufferevent_enable(bev, EV_READ);
    LPDWORD id;
    CreateThread(NULL, 0, SendThread, bev, 0, NULL);
}

int main()
{
#ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    (void)WSAStartup(wVersionRequested, &wsaData);
    evthread_use_windows_threads();
#endif
    //init server
    struct sockaddr_in serv;

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(8888);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);

    //创建 event_base
    struct event_base * base;
    base = event_base_new();

    //创建套接字
    //绑定
    //接收连接请求
    struct evconnlistener* listener;
    listener = evconnlistener_new_bind(base, cb_listener, base, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 36, (struct  sockaddr*)&serv, sizeof(serv));
    
    //启动循环监听
    event_base_dispatch(base);
    evconnlistener_free(listener);
    event_base_free(base);
    return 0;
}

新开的线程做的事情很简单,就是每秒钟主动往客户端发送一句话。

你可能感兴趣的:(libevent,libevent,tcp,服务器主动发送消息)