1. 程式人生 > >libevent學習一:windows上簡單的libevent例子

libevent學習一:windows上簡單的libevent例子

    這裡開發主要在windows上開發,熟悉的同學可以修改少量程式碼移植到linux上去,由於在windows上方便的原因,學習主要還是在windows上面開發,而且linevent是支援跨平臺的,所以也可以不用太在意。

    上一篇我們已經編譯好了libevent,版本是2.1.8,使用的vs 版本是vs2017。

    這裡給大家推薦一下:http://www.wangafu.net/~nickm/libevent-book/   ;有能力的可以去libevent的官網(主要是英語能力),官方寫得還是很好很全面的。

    這裡我們先了解一下libevent,libevent是一個事件驅動庫,不能僅說它是一個網路庫,在這裡我也作為一個初學者,和大家講一下我的瞭解,後面會持續更新。

    現在我們就把libevent當成支援網路通訊的事件驅動庫,來實現一個簡單的libevent服務客戶端程式:

客戶端:

流程:

    1、載入套接字型檔 (windows的socket通訊基礎可以知道);

    2、連線服務端;

    3、初始化一個event_base物件,建立一個event物件,將socket和處理接收輸出的回撥函式賦通過event_new賦值給event物件,然後將event物件加入事件迴圈;

    4、建立一個執行緒,接收介面輸入傳送給服務端;

    5、開啟事件迴圈;

    6、關閉wsa。

程式碼:

#include "stdafx.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include "event2/listener.h"
#include "event2/util.h"
#include "event2/event.h"
#include <event2/event-config.h>
#include <WinSock2.h>
#include <iostream>
#define IP_ADDRESS ("127.0.0.1")
#define PORT (9951)
int m_isrun = 0;
int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, char* msg);
void socket_read_cb(int fd, short events, void *arg);
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
	char t_cin[1024];
	int sockfd = (int)lpParameter;
	while (1) {
		memset(t_cin, 0, 1024);

		std::cin >> t_cin;

		if (strcmp(t_cin, "exit") == 0) {
			break;
		}
		cmd_msg_cb(sockfd, t_cin);
	}
	exit(1);
	return 0;
}
int main(int argc, char** argv)
{
	//載入套接字型檔  
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		return -1;
	}

	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		return -1;
	}
	//兩個引數依次是伺服器端的IP地址、埠號  
	int sockfd = tcp_connect_server(IP_ADDRESS, PORT);
	if (sockfd == -1)
	{
		perror("tcp_connect error ");
		return -1;
	}
	printf("connect to server successful\n");
	struct event_base* base = event_base_new();
	struct event *ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, NULL);
	event_add(ev_sockfd, NULL);
	//建立執行緒傳送資料
	HANDLE hThread1 = CreateThread(NULL, 0, Fun1Proc, (void*)sockfd, 0, NULL);
	CloseHandle(hThread1);
	event_base_dispatch(base);
	WSACleanup();
	printf("finished \n");
	return 0;
}
void cmd_msg_cb(int fd, char* msg)
{
	//把終端的訊息傳送給伺服器端  
	int ret = send(fd, (const char*)msg, strlen((char*)msg), 0);
	if (ret <= 0)
	{
		perror("read fail ");
		return;
	}
	printf("send:%s\n", (char*)msg);
}
void socket_read_cb(int fd, short events, void *arg)
{
	char msg[1024];
	//為了簡單起見,不考慮讀一半資料的情況  
	int len = recv(fd, msg, sizeof(msg) - 1, 0);
	if (len <= 0)
	{
		perror("read fail ");
		exit(1);
	}
	msg[len] = '\0';
	printf("recv %s from server\n", msg);
}
int tcp_connect_server(const char* server_ip, int port)
{
	int sockfd, status, save_errno;
	SOCKADDR_IN  server_addr;

	memset(&server_addr, 0, sizeof(server_addr));

	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(server_ip);
	server_addr.sin_port = htons(port);

	sockfd = ::socket(PF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
		return sockfd;


	status = ::connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

	if (status == -1)
	{
		save_errno = errno;   //清理  
		closesocket(sockfd);
		errno = save_errno; //the close may be error  
		return -1;
	}

	evutil_make_socket_nonblocking(sockfd);

	return sockfd;
}

服務端:

流程:

    1、載入套接字型檔

    2、初始化tcp的socket,監聽繫結埠;

    3、建立event_base和event物件,將等待連線的回撥函式和socket賦值給event物件,然後加入事件迴圈,開始事件迴圈;

    4、關閉套接字型檔。

程式碼:

#include<stdio.h>  
#include<string.h>  
#include<errno.h>
#include <signal.h>
#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include "event2/listener.h"
#include "event2/util.h"
#include "event2/event.h"
#include <event2/event-config.h>
#include <WinSock2.h>

#define IP_ADDRESS ("127.0.0.1")
#define PORT (9951)

void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int tcp_server_init(int port, int listen_num);

int main(int argc, char** argv)
{
	//載入套接字型檔  
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		//cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		//cout << "WSADATA version is not correct!" << endl;
		return -1;
	}
	int listener = tcp_server_init(PORT, 10);
	if (listener == -1)
	{
		perror(" tcp_server_init error ");
		return -1;
	}
	struct event_base* base = event_base_new();
	//新增監聽客戶端請求連線事件  
	struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
		accept_cb, base);
	event_add(ev_listen, NULL);
	event_base_dispatch(base);
	WSACleanup();
	return 0;
}

void accept_cb(int fd, short events, void* arg)
{
	evutil_socket_t sockfd;
	struct sockaddr_in client;
	socklen_t len = sizeof(client);
	sockfd = ::accept(fd, (struct sockaddr*)&client, &len);
	evutil_make_socket_nonblocking(sockfd);
	printf("accept a client %d\n", sockfd);
	struct event_base* base = (event_base*)arg;
	//僅僅是為了動態建立一個event結構體  
	struct event *ev = event_new(NULL, -1, 0, NULL, NULL);
	//將動態建立的結構體作為event的回撥引數  
	event_assign(ev, base, sockfd, EV_READ | EV_PERSIST,
		socket_read_cb, (void*)ev);	
	event_add(ev, NULL);
}

void socket_read_cb(int fd, short events, void *arg)
{
	char msg[4096];
	struct event *ev = (struct event*)arg;
	int len = recv(fd, msg, sizeof(msg) - 1, 0);
	if (len <= 0)
	{
		printf("some error happen when read\n");
		event_free(ev);
		closesocket(fd);
		return;
	}
	msg[len] = '\0';
	printf("recv the client msg: %s\n", msg);
	char reply_msg[4096] = "I have recvieced the msg: ";
	strcat(reply_msg + strlen(reply_msg), msg);
	int ret = send(fd, reply_msg, strlen(reply_msg), 0);
}

int tcp_server_init(int port, int listen_num)
{
	int errno_save;
	int listener;
	listener = ::socket(AF_INET, SOCK_STREAM, 0);
	if (listener == -1)
		return -1;
	//允許多次繫結同一個地址。要用在socket和bind之間  
	evutil_make_listen_socket_reuseable(listener);
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = 0;
	sin.sin_port = htons(port);
	if (::bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		errno_save = errno;
		evutil_closesocket(listener);
		errno = errno_save;
		return -1;
	}
	if (::listen(listener, listen_num) < 0) {
		errno_save = errno;
		evutil_closesocket(listener);
		errno = errno_save;
		return -1;
	}
	//跨平臺統一介面,將套接字設定為非阻塞狀態  
	evutil_make_socket_nonblocking(listener);
	return listener;
}

相關推薦

libevent學習windows簡單libevent例子

    這裡開發主要在windows上開發,熟悉的同學可以修改少量程式碼移植到linux上去,由於在windows上方便的原因,學習主要還是在windows上面開發,而且linevent是支援跨平臺的,所以也可以不用太在意。     上一篇我們已經編譯好了libevent,

TestLink學習Windows搭建Apache+MySQL+PHP環境

左右 mysql數據庫 uri 成了 -h 時區 站點 ngx php_curl PHP集成開發環境有很多,如XAMPP、AppServ......只要一鍵安裝就把PHP環境給搭建好了。但這種安裝方式不夠靈活,軟件的自由組合不方便,同時也不利於學習。所以我還是喜歡手工搭建

機器學習備註Windows執行Mask_RCNN模型

這些模型在Linux上執行比較方便,不過想起Windows便捷的各種工具,總是忍不住弄過來, 原模型的地址在這, https://github.com/matterport/Mask_RCNN 需要用到的工具pycocotools在這 不過要注意,這個pycocotool

RabbitMQ的學習()Windows下安裝及配置RabbitMQ,erlang環境變數

前言:從本章開始,慢慢去了解並深化架構師成長路上所需要的基礎框架:訊息中介軟體; 從這裡開始,會一直往下學習,從what to do到how to do 到why to do。先學會如何使用中介軟體,到後面慢慢去體會中介軟體原理及精髓,然後到最後自己再搭建一個簡單的中介軟體框架。這是該分類的

JBPM學習()實現一個簡單的工作流例子全過程

最近發現這篇文章挺多人看的,也挺多人有疑問,我很想幫你們解答,但是很無奈,這篇文章只是我當時在自學時看的一個教程的記錄,當時對JBPM也沒有深入去學習,並且到現在也已經快4年了,這期間我都沒用過JBPM

python django學習簡單註冊/登陸/session

rfi bmi ews exception eth mar %u objects 未使用 註冊 登陸 session user.html 未使用{{useform}}而使用{{ userform.password }}形式便於後期css樣式 <!DOCTYPE htm

libevent學習之一libevent原始碼的特點和結構

1.特點 Libevent是一個用於開發可擴充套件性網路伺服器的基於事件驅動(event-driven)模型的網路庫。Libevent有幾個顯著的特點:  (1)事件驅動(event-driven),高效能; (2)輕量級,專注於網路,不如 ACE 那麼臃腫龐大;  (3)原始碼

深度學習搭建簡單的全連線神經網路

深度學習一:搭建簡單的全連線神經網路 新手入門學習神經網路,嘗試搭建淺層的全連線神經網路,廢話不多說,上主題(文章左後會貼上全部程式碼): 實驗環境:Python3+Pycharm 一個神經網路分為輸入層、隱藏和輸出層,先實現一個單隱藏層的神經網路,輸入為隨機向量x,通過神經網路,擬合隨機

EF6學習筆記code-first簡單創建數據庫、表的一些過程

time img install 類名 開啟 屬性 bject nbsp 程序包管理 我的EF學習筆記是按照 汪鵬(網名Jeffcky) 大俠《你必須掌握的Entity Framework 6.x與Core 2.0》來弄的。 這也是我第一篇博客,感覺這東西不能亂寫啊,算

EF6學習筆記code-first簡單建立資料庫、表的一些過程

  我的EF學習筆記是按照 汪鵬(網名Jeffcky) 大俠《你必須掌握的Entity Framework 6.x與Core 2.0》來弄的。 這也是我第一篇部落格,感覺這東西不能亂寫啊,算了,幹吧。 EF我之前是做過的,但是隻是一些零碎的東西,不成系統。 EF是什麼呢?ORM框架objec

Linux學習筆記()Win10用VMware虛擬機器安裝Linux-CentOS

這周開始Linux的學習,教材參考鳥哥的Linux私房菜,系統本來想用UBUNTU,不過為了方便還是使用教材推薦的CENTOS,下面附上自己的安裝過程。一、 VMware簡介:VMware是一個虛擬PC的軟體,可以在現有的操縱系統上虛擬出一個新的硬體環境,相當於模擬出一臺新的

Python 新手實戰之機器學習實現簡單驗證碼識別()用PIL簡單繪製驗證碼

驗證碼生成 from PIL import Image, ImageDraw, ImageFont import random, os def draw(): #隨機生成背景顏色 (RGB顏色範圍為0-255,越高越接近白色),背景顏色不宜過深,

Android多媒體學習Android中Image的簡單例項。

在多媒體應用中,Image是最基礎的功能模組,接下來我們將看看在Android中是如何獲取和儲存Image的。Android內嵌的Image獲取和儲存功能,可以讓我們對於整個媒體框架有個比較全面的瞭解,同時為audio和video的學習打下基礎。 一、Image的獲取可以通過

MongoDB的學習與應用安裝並簡單測試MongoDB

Document DatabaseA record in MongoDB is a document, which is a data structure composed of field and value pairs. MongoDB documents are similar to JSON obje

Apollo學習()在本地Windows系統下搭建Apollo配置中心

說明 在學習Spring Cloud時,學習了Spring Cloud Config作為配置中心來實現微服務例項的配置。但是由於Config自身的侷限性,公司採用了攜程的Apollo作為配置中心,於是通過官方文件和網上資料進行了學習,在這裡進行記錄總結下。 正文

FiddlerScript學習改動Request或Response

directed author use lock 改變 請求 including ogl cookies 前兩天因項目須要,簡單看了一下FiddlerScript,功能挺強的。今天有時間細致看一下,做個筆記。 改動Request或Response 改動Request和R

WPF學習XAML的資源(Resources)結構

pac logs value 改變 linear sha -a writer arp 一個初學者,把知識做個積累,如果有不對的地方,還請高手指出,謝謝! 先看一段代碼:(下面是以Window WPF進行講解,如果是Web 的話就把<Window改為<Page 而

tomcat源碼學習導入eclipse

tps trunk 沒有 asf rop base https distrib 打開 一、下載源碼 進入官網http://tomcat.apache.org,點擊Download>tomcat9>Source Code Distributions>zip 

前端知識學習 CSS基礎

分隔 color html元素 http 方式 瀏覽器 單位 工作 分離 一.CSS概述     css指的是層疊樣式表,樣式定義如何顯示HTML元素,樣式通常存儲在樣式表中,   把樣式添加到HTML4.0中,是為了解決內容和表現分離的問題。外部樣式表通常存儲在css文件

Spring boot 學習 認識Spring boot

XML 過程 maven 認識 很多 團隊 但是 日誌文件 pivotal 什麽是spring boot Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人