【編程筆記】libevent探究——代碼篇

代碼

libevent網上使用栗子很多,最基本的使用無外乎幾個接口的使用,附上libevent文檔說明:https://www.monkey.org/~provos/libevent/doxygen-2.0.1/index.html

在libevent基礎上,我們編寫:服務端&客戶端來進行理解,最基本的用法。

  • 服務端的創建
//#define _USE_WINSOCK

// EV_READ : 只要網絡緩衝中還有數據,回調函數就會被觸發
// EV_WRITE : 只要塞給網絡緩衝的數據被寫完,回調函數就會被觸發
// EV_PERSIST : 不指定這個屬性的話,回調函數被觸發後事件會被刪除
void CServerSocket::CreateServer( const DWORD v_dwPort )
{
	SOCKADDR_IN saServerAddress;
	ZERO_MEMORY(&saServerAddress, sizeof(SOCKADDR_IN));
	saServerAddress.sin_family = AF_INET;
	saServerAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	saServerAddress.sin_port = htons(v_dwPort);

#ifdef _USE_WINSOCK //採用傳統的bind和listen
	evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(sock, (SOCKADDR*)&saServerAddress, sizeof(saServerAddress)) == SOCKET_ERROR)
	{
		printf("Create Server Failed.[bind]Err=<%d>\n", WSAGetLastError());
		return;
	}

	if (listen(sock, 10) == SOCKET_ERROR)
	{
		printf("Create Server Failed.[listen]Err=<%d>\n", WSAGetLastError());
		return;
	}

	m_pBase = event_base_new();
	struct event* listenEvent = event_new(m_pBase, sock, EV_READ | EV_PERSIST, do_accept_cb, (void*)m_pBase);
	event_add(listenEvent, NULL);

	printf("Create Server Success. Listen At <%d>\n", v_dwPort);
	printf("Wait For Client Connect...\n");

	event_base_dispatch(m_pBase); //阻塞
	event_free(listenEvent);
	event_base_free(m_pBase);
#else //採用libevent的listen事件,綁定端口及監聽
	m_pBase = event_base_new();
	struct evconnlistener* listener = evconnlistener_new_bind(m_pBase, listener_cb, (void*)m_pBase, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
		-1, (SOCKADDR*)&saServerAddress, sizeof(saServerAddress));
	
	printf("Create Server Success. Listen At <%d>\n", v_dwPort);
	printf("Wait For Client Connect...\n");

	event_base_dispatch(m_pBase);
	evconnlistener_free(listener);
	event_base_free(m_pBase);
#endif

	printf("Close Server...\n");
}

void listener_cb(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int socklen, void* ctx);
void do_accept_cb(evutil_socket_t sock, short event, void* ctx);
void read_cb_s(struct bufferevent *bev, void* ctx);
void write_cb_s(struct bufferevent *bev, void *ctx);
void event_cb_s(struct bufferevent *bev, short what, void* ctx);
//////////////////////////////////////////////////////////////////////////
void listener_cb(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int socklen, void* ctx)
{	
	printf("Accept Cilent=<%u><%s><%d>\n", fd, inet_ntoa(((sockaddr_in*)addr)->sin_addr), ntohs(((sockaddr_in*)addr)->sin_port));

	struct event_base* base = (struct event_base*)ctx;
	struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	bufferevent_setcb(bev, read_cb_s, write_cb_s, event_cb_s, base);
	bufferevent_enable(bev, EV_READ | EV_WRITE);
}

void do_accept_cb(evutil_socket_t sock, short event, void* ctx)
{
	struct event_base* base = (struct event_base*)ctx;
	evutil_socket_t fd;
	SOCKADDR_IN saClientAddress;
	int iLen = sizeof(saClientAddress);
	fd = accept(sock, (SOCKADDR*)&saClientAddress, &iLen);
	if (SOCKET_ERROR == fd)
	{
		printf("Accept Client Connect Failed.[accept]Err=<%d>\n", WSAGetLastError());
		return;
	}

	printf("Accept Cilent=<%u><%s><%d>\n", fd, inet_ntoa(saClientAddress.sin_addr), ntohs(saClientAddress.sin_port));

	struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	bufferevent_setcb(bev, read_cb_s, write_cb_s, event_cb_s, ctx);
	bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}

void read_cb_s(struct bufferevent *bev, void *ctx)
{
	evutil_socket_t fd = bufferevent_getfd(bev);
	char szLine[MAX_PATH + 1] = {0};
	int n = 0;
	while (n = bufferevent_read(bev, szLine, MAX_PATH))
	{
		szLine[n] = '\0';
		printf("fd=%u, read line: %s\n", fd, szLine);
		//bufferevent_write(bev, szLine, n);
	}
}

void write_cb_s(struct bufferevent *bev, void *ctx)
{
	printf("Send Welcome...\n");
	//bufferevent_write(bev, "Welcome...", 10);
}

void event_cb_s(struct bufferevent *bev, short what, void *ctx)
{
	evutil_socket_t fd = bufferevent_getfd(bev);
	printf("fd=%u, ", fd);
	if (what & BEV_EVENT_TIMEOUT)
	{
		printf("Timed out\n"); //if bufferevent_set_timeouts() called
	}
	else if (what & BEV_EVENT_EOF)
	{
		printf("connection closed\n");
	}
	else if (what & BEV_EVENT_ERROR)
	{
		printf("some other error\n");
	}
	bufferevent_free(bev);
}
  • 客戶端的創建
void CClientSocket::CreateClient(const char* v_szServerIP, const DWORD v_dwServerPort)
{
	SOCKADDR_IN saAddress;
	ZERO_MEMORY(&saAddress, sizeof(saAddress));
	saAddress.sin_family = AF_INET;
	saAddress.sin_addr.S_un.S_addr = inet_addr(v_szServerIP);
	saAddress.sin_port = htons(v_dwServerPort);

	SOCKET fd = socket(AF_INET, SOCK_STREAM, 0);
	if (SOCKET_ERROR == fd)
	{
		printf("Create Client Failed.[socket]Err=<%d>\n", WSAGetLastError());
		return;
	}

	m_pBase = event_base_new();
	struct bufferevent* bev = bufferevent_socket_new(m_pBase, fd, BEV_OPT_CLOSE_ON_FREE);
	bufferevent_setcb(bev, read_cb_c, write_cb_c, event_cb_c, NULL);
	bufferevent_socket_connect(bev, (SOCKADDR*)&saAddress, sizeof(saAddress));
	bufferevent_enable(bev, EV_READ | EV_WRITE);
	event_base_dispatch(m_pBase);
	event_base_free(m_pBase);
}

void read_cb_c(struct bufferevent *bev, void *ctx)
{
	//取得輸入、輸出緩衝區
	struct evbuffer* input = bufferevent_get_input(bev);
	struct evbuffer* output = bufferevent_get_output(bev);
	
	//從input緩衝區前面複製並移除sizeof(buffer)字節數據到buffer處的內存處
	//如果字節數不夠sizeof(buffer),則複製並移除所有字節
	char buffer[1024] = {0};
	int n = 0;
	while ( (n = evbuffer_remove(input, buffer, sizeof(buffer))) > 0 )
	{
		printf("Client recv:%s\n", buffer);
	}
	
	char szSend[] = "Client send ok...";
	evbuffer_add(output, szSend, strlen(szSend));
}

void write_cb_c(struct bufferevent *bev, void *ctx)
{
	printf("Send Ok....\n");
}

void event_cb_c(struct bufferevent *bev, short what, void *ctx)
{
	if (what & BEV_EVENT_EOF) //遇到文件結束指示
	{
		bufferevent_free(bev);
		printf("Connection closed.\n");
	}
	else if (what & BEV_EVENT_ERROR) //操作發生錯誤
	{
		bufferevent_free(bev);
		printf("Got an error on the connection: %d\n", WSAGetLastError());
	}
	else if (what & BEV_EVENT_TIMEOUT) //超時
	{
		bufferevent_free(bev);
		printf("Connection timeout.\n");
	}
	else if (what & BEV_EVENT_CONNECTED) //連接已經完成
	{
		printf("connect succeed.\n");
		//客戶端鏈接成功後,給服務器發送第一條消息  
		char *reply_msg = "I receive a message from client";
		bufferevent_write(bev, reply_msg, strlen(reply_msg));
	}
}

libevent處理網絡事件,都是採用回調的方式處理,所以需要註冊讀寫回調等。詳細等後續深入研究源碼,這邊貼上,此項目代碼鏈接:https://download.csdn.net/download/fzuim/11041669

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章