消息转发模块的简单实现

本文档展示了如何使用Libevent库创建一个监听9999端口的服务器,当接收到连接请求时,它会为每个连接创建一个Bufferevent,并将所有连接的事件回调注册到同一个读取事件处理函数。读取事件处理函数会将接收到的数据广播到其他所有连接。同时,还提供了一个使用Epoll的TCP服务器实现,监听8851端口,接受连接并转发数据。这两个例子都涉及到并发处理和网络通信的基本机制。
摘要由CSDN通过智能技术生成
//libevent实现
#include <cstdio>
#include <event2/event.h>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/bufferevent.h>
#include <unistd.h>
#include <list>
#include <iostream>

std::list<struct bufferevent*> buffd_list;

//读事件
void ReadEventCallback(struct bufferevent* bev, void* ctx)
{
    std::cout << "Call Read Event Callback funcation" << std::endl;
    char buf[1024];
    int len = bufferevent_read(bev, buf, sizeof(buf));
    //从list获取bev逐个发送
    // 回复数据
    int i = 0;
    for (auto& buflist : buffd_list){
        if (buflist != bev){
            bufferevent_write(buflist, buf, len);
            i++;
            std::cout << "send data: " << buf << " send number: " << i << std::endl;
        }
        
    }
}

//写事件
void WriteEventCallback(struct bufferevent* bev, void* ctx)
{
    printf("写事件回调函数被调用");
}

//事件回调
void EventsCallback(struct bufferevent* bev, short events, void* ctx)
{
    if (events & BEV_EVENT_ERROR)
    {
        printf("发生了某些错误...\n");
    }
    else if (events & BEV_EVENT_EOF)
    {
        printf("客户端断开了连接...\n");
        // 释放缓冲区
        bufferevent_free(bev);
    }
}


//监听事件回调,要让这个事件循环
void ListenerCallBack(struct evconnlistener* listener,  //注意传参 1.监听器
    evutil_socket_t sock,                               //2.通信文件描述符
    struct sockaddr* addr,                              //3.地址结构体
    int len,void* ptr)                                  //4.sock大小和event_base的指针
{


    // 得到了通信文件描述符 sock
    // 将sock封装 -> bufferevent
    struct event_base* base = (struct event_base*)ptr;
    struct bufferevent* bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
    //将bev放入list***
    buffd_list.push_back(bev);
    // 给bufferevent设置回调函数
    bufferevent_setcb(bev, ReadEventCallback, WriteEventCallback, EventsCallback, NULL);
    // 设置读缓冲区可用
    bufferevent_enable(bev, EV_READ);
    std::cout << "push back one sock" << std::endl;

}

int main()
{
    //建立框架
    struct event_base* Event_Fream = event_base_new();
    std::cout << "build event fream" << std::endl;

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);    // 服务器需要绑定9999端口
    addr.sin_addr.s_addr = INADDR_ANY;

    //创建监听
    struct evconnlistener* Listener;
    Listener = evconnlistener_new_bind(Event_Fream, ListenerCallBack, Event_Fream,
        LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,  //flag
        -1, (struct sockaddr*)&addr, sizeof(addr));
    std::cout << "start listen port: " << addr.sin_port;

    //开启监听事件循环
    event_base_dispatch(Event_Fream);
    std::cout << "start event loop" << std::endl;

    //释放资源
    evconnlistener_free(Listener);
    event_base_free(Event_Fream);

    return 0;
}
//封装系统接口
//server.h
#pragma once
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <list>
#include <sys/epoll.h>

class tcp_server
{
public:
	bool server_listen();
	void server_run();
	void server_close();
	static tcp_server* getinstance();
private:
	struct sockaddr_in addr;
	int listen_fd;
	int connect_fd;
	std::list<int> accept_list;
	struct epoll_event epr[100];
	int epoll_tree;
	int epoll_count;
	tcp_server();
	tcp_server(const tcp_server&);
	static tcp_server* server_ptr;
};

// server.cpp
#include "tcp_server.h"

tcp_server* tcp_server::server_ptr = NULL;
tcp_server::tcp_server()
{}

tcp_server::tcp_server(const tcp_server&)
{}

bool tcp_server::server_listen()
{
	listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_fd < 0) {
		perror("socket");
		return false;
	}
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(8851);
	int ret = bind(listen_fd,(struct sockaddr*)&addr,sizeof(addr));
	if (ret < 0) {
		perror("bind");
		return false;
	}
	ret = listen(listen_fd,128);
	if (ret < 0) {
		perror("listen");
		return false;
	}
	//创建epoll树
	epoll_tree = epoll_create(1);
	//上epoll树
	struct epoll_event evt;
	evt.data.fd = listen_fd;
	evt.events = EPOLLIN;
	ret = epoll_ctl(epoll_tree, EPOLL_CTL_ADD, listen_fd, &evt);
	if (ret == -1)
	{
		perror("epoll_ctl");
		std::cout << "listen fd:" << listen_fd << std::endl;
		std::cout << "epoll_tree fd:" << epoll_tree << std::endl;
		return false;
	}
	std::cout << "established listen and create epoll tree" << std::endl;
	return true;
}

void tcp_server::server_run()
{
	if (!server_listen())
	{
		std::cout << "server listen error" << std::endl;
		server_close();
		return;
	}
	int size = sizeof(epr) / sizeof(epoll_event);
	//将所有主动连接的socket都记录并监听
	std::cout << "main server loop start" << std::endl;
	while(1) {
		epoll_count = epoll_wait(epoll_tree, epr, size, -1);
		for (int i = 0; i < epoll_count; i++) {
			if (epr[i].data.fd == listen_fd) { //判断是不是监听文件,如果监听文件有变化则将对应的通信文件添加到epoll树和vector
				int cfd = accept(listen_fd, NULL, NULL);
				if (cfd < 0){
					perror("accept");
					continue;
				}
				epoll_event evt;
				evt.data.fd = cfd;
				evt.events = EPOLLIN;
				int ret = epoll_ctl(epoll_tree, EPOLL_CTL_ADD, cfd, &evt);
				if (ret == -1)
				{
					perror("epoll_ctl");
					return ;
				}
				accept_list.push_back(cfd);
				std::cout << "Add a link file" << std::endl;
			}
			else{
				//获取消息并加入消息队列或转发
				char buf[100] = { 0 };
				int count = recv(epr[i].data.fd, buf, sizeof(buf), 0);
				if (count <= 0 ){
					perror("recv");
					continue;
				}
				for (auto it : accept_list){
					//转发数据
					if (it != epr[i].data.fd){
						send(it, buf, count, 0);
						std::cout << "send data: " << buf << " file describes: " << it << std::endl;
					}
				}
			}
		}
	}
}

void tcp_server::server_close()
{
	for (auto it : accept_list){
		close(it);
	}
	close(listen_fd);
	close(epoll_tree);
}

tcp_server* tcp_server::getinstance()
{
	if (server_ptr != nullptr){
		return server_ptr;
	}
	server_ptr = new tcp_server;
	return server_ptr;
}
//main.cpp
#include <cstdio>
#include "tcp_server.h"
int main()
{
    tcp_server* tcp = tcp_server::getinstance();
    tcp->server_run();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值