Heim >php教程 >php手册 >Implementieren Sie einen einfachen Libevent-Server in einer Windows-Umgebung

Implementieren Sie einen einfachen Libevent-Server in einer Windows-Umgebung

WBOY
WBOYOriginal
2016-10-19 10:19:311352Durchsuche

Ich habe Libevent kürzlich gelernt, weil ich ein Windows-System verwende. Leider gibt es nur sehr wenige Programme, die unter VS referenziert werden können. Nachdem ich auf viele Blog-Artikel verwiesen habe. Ich habe ein einfaches Libevent-Serverprogramm erforscht und geschrieben. Und ich habe im Internet ein einfaches Client-Programm gefunden und den Code erfolgreich getestet. Machen Sie sich noch heute hier eine Notiz.

Libevent ist in der Tat eine sehr nützliche Sache, und ich lerne noch. In Zukunft werde ich Multithreading von Libevent unter Windows implementieren. Heute werde ich das, was ich erstellt habe, nur zu Ihrer Information veröffentlichen. Zusammengestellt und vs2015 weitergegeben.

Es ist standardmäßig Single-Threaded (kann bei Bedarf als Multi-Threaded konfiguriert werden). Jeder Thread verfügt über eine und nur eine Ereignisbasis, entsprechend einer struct event_base-Struktur (und). Der damit verbundene Ereignismanager wird vom Zeitplan zum Hosten einer Reihe von Ereignissen verwendet. Dies kann natürlich mit der Prozessverwaltung des Betriebssystems verglichen werden. Wenn ein Ereignis auftritt, ruft event_base die an das Ereignis gebundene Funktion zum entsprechenden Zeitpunkt (nicht unbedingt sofort) auf (übergibt einige vordefinierte Parameter sowie einen beim Binden angegebenen Parameter) und kehrt nach der Ausführung der Funktion zu zurück den Zeitplan für andere Veranstaltungen.

//Eine event_base erstellen

struct event_base *base = event_base_new();

assert(base != NULL);

In event_base gibt es eine Schleife, die Systemaufrufe wie epoll/kqueue blockiert, bis ein oder mehrere Ereignisse eintreten, und diese Ereignisse dann verarbeitet. Natürlich müssen diese Ereignisse an diese event_base gebunden sein. Jedes Ereignis entspricht einem Strukturereignis, das auf ein fd- oder POSIX-Semaphor lauscht (hier wird nur fd besprochen, andere finden Sie im Handbuch). struct event verwendet event_new zum Erstellen und Binden, verwenden Sie event_add zum Aktivieren von:

//Erstellen und binden Sie ein Ereignis

struct event *listen_event;

//Parameter: event_base, überwachter fd, Ereignistyp und -attribute, gebundene Rückruffunktion, Parameter für die Rückruffunktion

listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, ( void*)base);

//Parameter: Ereignis, Timeout-Zeit (struct timeval *type, NULL bedeutet keine Timeout-Einstellung)

event_add(listen_event, NULL) ;

Hinweis: Zu den von libevent unterstützten Ereignissen und Attributen gehören (implementiert mit Bitfield, also verwenden Sie |, um sie zu kombinieren)

(a) EV_TIMEOUT: Timeout

(b) EV_READ: Solange Daten im Netzwerkpuffer vorhanden sind, wird die Rückruffunktion ausgelöst

(c) EV_WRITE: Solange die in den Netzwerkpuffer eingefügten Daten geschrieben werden, wird die Rückruffunktion ausgelöst. Auslöser

(d) EV_SIGNAL: POSIX-Semaphor, siehe Handbuch

(e) EV_PERSIST: Wenn dieses Attribut nicht angegeben ist, wird das Ereignis gelöscht, nachdem die Callback-Funktion ausgelöst wurde

(f) EV_ET: Edge – Trigger-Flankentrigger, siehe EPOLL_ET

und dann müssen Sie die event_base-Schleife starten, damit Sie mit der Verarbeitung der auftretenden Ereignisse beginnen können. Der Ereignisbasisversand wird in der Schleife gestartet und die Schleife wird fortgesetzt, bis keine Ereignisse mehr vorhanden sind, die Aufmerksamkeit erfordern, oder die Funktion event_loopbreak() / event_loopexit() auftritt.

//Ereignisschleife starten

event_base_dispatch(base);

Konzentrieren Sie sich als Nächstes auf die an das Ereignis gebundene Rückruffunktion callback_func: Was ihr übergeben wird, ist ein Socket fd, ein Ereignistyp und ein Attribut bit_field und der letzte Parameter, der an event_new übergeben wird (sehen Sie sich die obigen Zeilen an, um zu überprüfen, dass event_base übergeben wird, tatsächlich geht es eher darum, eine Struktur zuzuweisen, alle relevanten Daten einzufügen und dann Werfen Sie es auf event_new und Sie können es hier erhalten. Der Prototyp ist:

typedef void(*event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)

Für einen Server ist der obige Prozess wahrscheinlich Diese Kombination :

1. listener = socket(), bind(), listen(), set nonblocking (fcntl-Einstellung kann im POSIX-System verwendet werden, Windows muss nicht eingestellt werden,

Eigentlich Libevent bietet ein einheitliches Paket (evutil_make_socket_nonblocking)

2. Erstellen Sie ein Ereignis, hosten Sie den Socket für event_base, geben Sie den zu überwachenden Ereignistyp an und binden Sie den entsprechenden Rückruf Funktion (und die Parameter, die ihr gegeben werden müssen)

    。对于listener socket来说,只需要监听EV_READ | EV_PERSIST

4. 启用该事件

5. 进入事件循环

-------------- -

6. (异步)当有client发起请求的时候,调用该回调函数,进行处理。

/*接下来关注下绑定到event的回调函数callback_func:传递给它的是一个socket fd、一个event类型及属性bit_field、以及传递给event_new的最后一个参数(去上面几行回顾一下,把event_base给传进来了,实际上更多地是分配一个结构体,把相关的数据都撂进去,然后丢给event_new,在这里就能取得到了)。*/

 

服务器端代码:Server.cpp

  1 #include  2 #include  3 #include  4 #include <string.h>  5 #include  6 #include<event2/event.h>  7 #include  8 #include  9 #include 10 #pragma comment (lib,"ws2_32.lib") 11 #include 12 #define LISTEN_PORT 9999 13 #define LIATEN_BACKLOG 32 14 using namespace std; 15 /********************************************************************************* 16 *                                      函数声明 17 **********************************************************************************/ 18 //accept回掉函数 19 void do_accept_cb(evutil_socket_t listener, short event, void *arg); 20 //read 回调函数 21 void read_cb(struct bufferevent *bev, void *arg); 22 //error回调函数 23 void error_cb(struct bufferevent *bev, short event, void *arg); 24 //write 回调函数 25 void write_cb(struct bufferevent *bev, void *arg); 26 /********************************************************************************* 27 *                                      函数体 28 **********************************************************************************/ 29 //accept回掉函数 30 void do_accept_cb(evutil_socket_t listener, short event, void *arg) 31 { 32     //传入的event_base指针 33     struct event_base *base = (struct event_base*)arg; 34     //socket描述符 35     evutil_socket_t fd; 36     //声明地址 37     struct sockaddr_in sin; 38     //地址长度声明 39     socklen_t slen = sizeof(sin); 40     //接收客户端 41     fd = accept(listener, (struct sockaddr *)&sin, &slen); 42     if (fd < 0) 43     { 44         perror("error accept"); 45         return; 46     } 47     printf("ACCEPT: fd = %u\n", fd); 48     ////注册一个bufferevent_socket_new事件 49     struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); 50     ////设置回掉函数 51     bufferevent_setcb(bev, read_cb, NULL, error_cb, arg); 52     ////设置该事件的属性 53     bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST); 54 } 55 ////read 回调函数 56 void read_cb(struct bufferevent *bev, void *arg) 57 { 58 #define MAX_LINE 256 59     char line[MAX_LINE + 1]; 60     int n; 61     //通过传入参数bev找到socket fd 62     evutil_socket_t fd = bufferevent_getfd(bev); 63     // 64     while (n = bufferevent_read(bev, line, MAX_LINE)) 65     { 66         line[n] = &#39;\0&#39;; 67         printf("fd=%u, read line: %s\n", fd, line); 68         //将获取的数据返回给客户端 69         bufferevent_write(bev, line, n); 70     } 71 } 72 ////error回调函数 73 void error_cb(struct bufferevent *bev, short event, void *arg) 74 { 75     //通过传入参数bev找到socket fd 76     evutil_socket_t fd = bufferevent_getfd(bev); 77     //cout << "fd = " << fd << endl; 78     if (event & BEV_EVENT_TIMEOUT) 79     { 80         printf("Timed out\n"); //if bufferevent_set_timeouts() called 81     } 82     else if (event & BEV_EVENT_EOF) 83     { 84         printf("connection closed\n"); 85     } 86     else if (event & BEV_EVENT_ERROR) 87     { 88         printf("some other error\n"); 89     } 90     bufferevent_free(bev); 91 } 92 ////write 回调函数 93 void write_cb(struct bufferevent *bev, void *arg) 94 { 95     char str[50]; 96     //通过传入参数bev找到socket fd 97     evutil_socket_t fd = bufferevent_getfd(bev); 98     //cin >> str; 99     printf("输入数据!");100     scanf_s("%d", &str);101     bufferevent_write(bev, &str, sizeof(str));102 }103 104 int main()105 {106     int ret;107     evutil_socket_t listener;108     WSADATA  Ws;109     //Init Windows Socket110     if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)111     {112         return -1;113     }114     listener = socket(AF_INET, SOCK_STREAM, 0);115     assert(listener > 0);116     evutil_make_listen_socket_reuseable(listener);117     struct sockaddr_in sin;118     sin.sin_family = AF_INET;119     sin.sin_addr.s_addr = 0;120     sin.sin_port = htons(LISTEN_PORT);121     if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {122         perror("bind");123         return 1;124     }125     if (listen(listener, 1000) < 0) {126         perror("listen");127         return 1;128     }129     printf("Listening...\n");130     evutil_make_socket_nonblocking(listener);131     struct event_base *base = event_base_new();132     assert(base != NULL);133     struct event *listen_event;134     listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept_cb, (void*)base);135     event_add(listen_event, NULL);136     event_base_dispatch(base);137     printf("The End.");138     return 0;139 }

客户端代码:Client.cpp

 1 /******* 客户端程序  client.c ************/ 2 #define _WINSOCK_DEPRECATED_NO_WARNINGS 3 #define _CRT_SECURE_NO_WARNINGS 4 #include 5 #include 6 #include 7 #include <string.h>       
 8 #include 9 #include10 #include11 12 #pragma comment (lib,"ws2_32.lib")13 int main(int argc, char *argv[])14 {15     WSADATA  Ws;16     //Init Windows Socket17     if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)18     {19         return 0;20     }21     int sockfd;22     char buffer[1024];23     struct sockaddr_in server_addr;24     struct hostent *host;25     int portnumber, nbytes;26 27     if ((host = gethostbyname("127.0.0.1")) == NULL)28     {29         fprintf(stderr, "Gethostname error\n");30         exit(1);31     }32 33     if ((portnumber = atoi("9999"))<0)34     {35         fprintf(stderr, "Usage:%s hostname portnumber\a\n", argv[0]);36         exit(1);37     }38 39     /* 客户程序开始建立 sockfd描述符  */40     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)41     {42         fprintf(stderr, "Socket Error:%s\a\n", strerror(errno));43         exit(1);44     }45 46     /* 客户程序填充服务端的资料       */47     memset(&server_addr,0, sizeof(server_addr));48     server_addr.sin_family = AF_INET;49     server_addr.sin_port = htons(portnumber);50     server_addr.sin_addr = *((struct in_addr *)host->h_addr);51 52     /* 客户程序发起连接请求         */53     if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1)54     {55         fprintf(stderr, "Connect Error:%s\a\n", strerror(errno));56         exit(1);57     }58 59     while (true)60     {61         char MESSAGE[] = "hello server..\n";62         //bufferevent_write(buf_ev,MESSAGE,strlen(MESSAGE));  
63         //  64         if (-1 == (::send(sockfd, MESSAGE, strlen(MESSAGE), 0)))65         {66             printf("the net has a error occured..");67             break;68         }69 70         if ((nbytes = recv(sockfd, buffer, 1024,0)) == -1)71         {72             fprintf(stderr, "read error:%s\n", strerror(errno));73             exit(1);74         }75 76         buffer[nbytes] = '\0';77         printf("I have received:%s\n", buffer);78         memset(buffer, 0, 1024);79 80         Sleep(2);81 82     }83     /* 结束通讯     */84     closesocket(sockfd);85     exit(0);86 87     return 0;88 }

 



回复内容:

[db:回复内容]

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:PHPExcel-bezogene VorgängeNächster Artikel:PHPExcel-bezogene Vorgänge