tcpserver

libevent VS下的编译
参考:https://blog.csdn.net/swartz_lubel/article/details/55809970
直接进入代码目录,输入nmake /f Makefile.nmake
在这里插入图片描述
报如下错误:
在这里插入图片描述
需要在 #include <stdint.h> 然后编译成功,生成libevent_core.lib libevent_extras.lib libevent.lib

导入如下库

#pragma comment(lib, "Iphlpapi.lib")
#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")

如果不导入Iphlpapi.lib,会报 error LNK2019: 无法解析的外部符号 _if_nametoindex@4,该符号在函数 _evutil_inet_pton_scope 中被引用



#include <iostream>
#include <string.h>
#include "event2/event.h"
#include "event2/bufferevent.h"
#include "event2/listener.h"

#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif
#pragma comment(lib, "Iphlpapi.lib")
#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;

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

	bufferevent_read(bev, buf, sizeof(buf));

	cout << "client " << ip << " say:" << buf << endl;


	//写数据给客户端
	const char *p = "Hello";
	bufferevent_write(bev, p, strlen(p) + 1);
}


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


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);
	cout << "bufferevent 资源已经被释放..." << endl;
}


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);
}

int main()
{
#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);
	(void)WSAStartup(wVersionRequested, &wsaData);
#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;
}

多线程的使用:
参考:https://www.cnblogs.com/zzyoucan/p/3970578.html

#ifndef LIBEVENT_SERVER_H
#define LIBEVENT_SERVER_H

#include "event2/event.h"
#include "event2/bufferevent.h"
#include "event2/listener.h"
#include <iostream>
#include <string.h>
#include <memory>
#include <vector>
#include "event.h"
#include <thread>
#include <mutex>
#include <queue>
#define THREAD_NUM 10

struct conn_queue_item
{
	int fd;
};


class LibEvtServer;
//保存线程的结构体
struct LibeventThread
{
	LibEvtServer* that;                            //用作传参
	std::shared_ptr<std::thread>   spThread;    // 线程
	struct event_base * thread_base;            // 事件根基
	struct event  notify_event;
	evutil_socket_t  notfiy_recv_fd;            // socketpair 接收端fd(工作线程接收通知)
	evutil_socket_t  notfiy_send_fd;            // socketpair 发送端fd(监听线程发送通知)
	std::mutex conn_mtx;                        //维护连接队列的锁
	std::queue<conn_queue_item>  conn_queue;    //conn_queue 是一个管理conn_queue_item的队列

};


class LibEvtServer
{
public:

	LibEvtServer();

	virtual ~LibEvtServer();

	void Init();

	void Listen();

public:
	event_base * m_base;
	int m_last_thread;
	std::vector<LibeventThread*> m_libevent_threads;
	evconnlistener * m_listener;
	/*main分发线程*/
	std::shared_ptr<std::thread>   m_spThread;
};


#endif

CPP文件

#include "LibEvtServer.h"
#include "event2/thread.h"
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#include <queue>
#include <mutex>
#include <thread>


LibEvtServer::LibEvtServer() :m_last_thread(-1)
{

}


LibEvtServer::~LibEvtServer()
{
	evconnlistener_free(m_listener);
	event_base_free(m_base);
}

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

	bufferevent_read(bev, buf, sizeof(buf));

	//写数据给客户端
	const char *p = "i am server, i received your msg!";
	bufferevent_write(bev, p, strlen(p) + 1);
}


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


void event_cb(struct bufferevent *bev, short events, void *arg)
{
	LibeventThread* plt = (LibeventThread*)arg;
	if (events & BEV_EVENT_EOF)
	{
		std::cout << "connection closed:";
	}
	else if (events & BEV_EVENT_ERROR)
	{
		std::cout << "some other error !";
	}

	bufferevent_free(bev);
	std::cout << "bufferevent 资源已经被释放...";
}


void notify_cb2(intptr_t fd, short which, void *pLibeventThread)
{
	//首先将socketpair的1个字节通知信号读出(这是必须的,在水平触发模式下如果不处理该事件,则会循环通知,直到事件被处理)
	char  buf[1];
	recv(fd, buf, 1, 0);//从sockpair的另一端读数据

	LibeventThread* plt = (LibeventThread*)pLibeventThread;
	conn_queue_item  item;

	//从自己的连接队列中取出连接数
	{
		//取出队列中的第一个元素
		std::lock_guard<std::mutex>  lck(plt->conn_mtx);
		item = plt->conn_queue.front();
	}

	//创建每个socket的bufferevent
	auto bev = bufferevent_socket_new(plt->thread_base, item.fd, BEV_OPT_THREADSAFE);


	//设置接收、状态改变 回调函数
	//给bufferevent缓冲区设置回调
	bufferevent_setcb(bev, read_cb, NULL, event_cb, plt);
	bufferevent_enable(bev, EV_READ | EV_WRITE);
}

void listener_cb2(evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
	LibEvtServer *pLibThis = (LibEvtServer*)user_data;
	if (!pLibThis)
	{
		return;
	}
	int cur_thread = (pLibThis->m_last_thread + 1) % THREAD_NUM; // 轮循选择工作线程
	pLibThis->m_last_thread = cur_thread;

	conn_queue_item item;
	item.fd = fd;
	//item.ch2 = NULL;

	auto  plt = pLibThis->m_libevent_threads[cur_thread];
	{
		//向线程的队列中放入一个item,每个线程有个队列,保存连接的socketfd
		std::lock_guard<std::mutex> lock(plt->conn_mtx);
		plt->conn_queue.push(item);
	}
	//激活读线程的读事件
	send(plt->notfiy_send_fd, "c", 1, 0);
}

void LibEvtServer::Listen()
{
#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);
	(void)WSAStartup(wVersionRequested, &wsaData);
#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);

	m_listener = evconnlistener_new_bind(m_base, listener_cb2, (void*)this,
		LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
		(struct sockaddr*)&serv,
		sizeof(serv));
	if (!m_listener)
	{
		return;
	}
	m_spThread.reset(new std::thread([]
		(void* arg)
	{
		//启动循环监听
		event_base *base = (event_base*)arg;
		event_base_dispatch(base);
	}, m_base));
	
	
}

void LibEvtServer::Init()
{
	//event支持windows下线程的函数
	int hr = evthread_use_windows_threads();
	//创建 event_base
	m_base = event_base_new();
	if (!m_base) {
		std::cout<<"Could not initialize libevent!\n";
		return;
	}

	for (int i = 0; i < THREAD_NUM; i++)
	{
		LibeventThread *ptLib = new LibeventThread;
	
		//创建一个socketpair即可与互相通信的两个socket,保存在fds里面
		evutil_socket_t fds[2];
		if (evutil_socketpair(AF_INET, SOCK_STREAM, 0, fds) < 0)
		{
			std::cout << "创建socketpair失败\n";
			return;
		}
		//设置成无阻赛的socket
		evutil_make_socket_nonblocking(fds[0]);
		evutil_make_socket_nonblocking(fds[1]);

		ptLib->notfiy_recv_fd = fds[0];
		ptLib->notfiy_send_fd = fds[1];
		ptLib->thread_base = event_base_new();
		//给每个libevent线程设置连接通知回调函数。
		ptLib->that = this;
		//设置线程事件notify_event
		event_set(&ptLib->notify_event, ptLib->notfiy_recv_fd,//EV_READ表示只要这个socket可读就调用notify_cb函数
			EV_READ | EV_PERSIST, notify_cb2, ptLib);
		//设置事件和event_base的关系
		event_base_set(ptLib->thread_base, &ptLib->notify_event); // 设置事件的从属关系(相当于指明事件属于哪个event_base)
		//添加事件
		event_add(&ptLib->notify_event, 0); // 正式添加事件      

		m_libevent_threads.push_back(ptLib);
	}

	//开始创建并启动线程
	for (int i = 0; i < THREAD_NUM; ++i)
	{
		m_libevent_threads[i]->spThread.reset(new std::thread([]
			(void* arg)
		{
			LibeventThread* me = (LibeventThread*)arg;
			event_base_loop(me->thread_base, 0);
		}, m_libevent_threads[i]));
	}
}


调用如下:

#include <iostream>
#include "LibEvtServer.h"

int main()
{
	LibEvtServer server;
	server.Init();
	server.Listen();
	return 0;
}

工程代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值