libevent(六)网络服务器事件(水平触发与边缘触发)

libevent(六)网络服务器事件(水平触发与边缘触发)
通过如下示例,主要演示了水平触发与边缘触发的原理以及效果。

#include <iostream>
#include <event2/event.h>
#include <thread>
#ifndef _WIN32
#include <signal.h>
#endif
#include <errno.h>
#include <string.h>
#define SPORT 5001
using namespace std;


//正常断开连接也会进入,超时会进入
void client_cb(evutil_socket_t s, short w, void* arg) {
	// 边缘触发(发有数据时,只进入一次,大数据量请求时再使用边缘触发)
	cout << "--->" << flush;

	// 水平触发LT,只要有数据未处理,会一直进入该函数,简单,一般情况使用
	event* ev = (event*)arg;
	//判断超时事件
	if (w&EV_TIMEOUT)
	{
		cout << "EV_TIMEOUT event_free" << endl;
		event_free(ev);
		evutil_closesocket(s);
		return;
	}
	char buf[5] = { 0 };
	int len = recv(s, buf, sizeof(buf) - 1, 0);
	if (len>0)
	{
		cout << "buf>>> " << buf << endl;
		send(s, "ok", 2, 0);
	}
	else
	{	
		//需要清理event
		cout << "..." << endl;
		event_free(ev);
		evutil_closesocket(s);
	}
}

void listen_cb(evutil_socket_t s, short w, void*arg) {
	cout << "====listen_cb======" << endl;

	sockaddr_in sin;
	socklen_t size = sizeof(sin);
	//读取连接信息
	evutil_socket_t client = accept(s, (sockaddr*)&sin, &size);
	char ip[16] = { 0 };
	evutil_inet_ntop(AF_INET, &sin.sin_addr, ip, sizeof(ip) - 1);
	cout << "client ip is:" << ip << endl;

	//客户端数据读取事件
	event_base* base = (event_base*)arg;

	//水平触发(客户端无接受发送时,一直处于客户端client_cb函数内)
	event* ev = event_new(base, client, EV_READ | EV_PERSIST, client_cb, event_self_cbarg());

	//边缘触发
	//event* ev = event_new(base, client, EV_READ | EV_PERSIST|EV_ET, client_cb, event_self_cbarg());
	timeval t = { 10,0 };
	event_add(ev, &t);

}


int main(int argc, char** argv) {
#if _WIN32
	//windowns 初始化socket库
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
#else
	//linux 忽略管道信号,发送数据给已关闭的socket
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif

	//创建socket(使用tcp/ip协议)
	event_base* base = event_base_new();
	evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, 0); 
	if (sock<=0)
	{
		cout << "socket error: " << strerror(errno) << endl;
		return -1;
	}

	//设置地址复用和非阻塞
	evutil_make_socket_nonblocking(sock);
	evutil_make_listen_socket_reuseable(sock);

	// 绑定端口与地址
	sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SPORT);
	int re = ::bind(sock, (sockaddr*)&sin, sizeof(sin));
	if (re!=0)
	{
		cerr << "bind error: " << strerror(errno) << endl;
		return -1;
	}

	//开始监听(全局监听)
	listen(sock, 10);

	//开始接受连接事件(default水平触发)
	event* ev = event_new(base, sock, EV_READ | EV_PERSIST, listen_cb, base);
	event_add(ev, 0);

	//进入事件主循环
	event_base_dispatch(base);
	evutil_closesocket(sock);
	event_base_free(base);
#ifdef _WIN32
	WSACleanup();
#endif // _WIN32
	return 0;
}

上一篇:字符数组与字符串——C语言描述


下一篇:Ch1 估计方法