目次
回复内容:
ホームページ php教程 php手册 Windows環境にシンプルなlibeventサーバーを実装する

Windows環境にシンプルなlibeventサーバーを実装する

Oct 19, 2016 am 10:19 AM

私は Windows システムを使用しているため、最近 Libevent を学習し始めました。残念ながら、VS で参照できるプログラムはほとんどありません。たくさんのブログ記事を参考にした結果。私は簡単な Libevent Server プログラムを調査して作成しました。そして、インターネット上で簡単なクライアント プログラムを見つけ、コードのテストに成功しました。今日はここにメモしておきます。

Libevent は確かに非常に便利なもので、将来的には Windows 上で Libevent のマルチスレッドを実装する予定です。今日は参考のために私が作成したものを投稿します。コンパイルされ、vs2015 に渡されました。

デフォルトではシングルスレッドです (必要に応じてマルチスレッドとして構成できます)。各スレッドには structevent_base 構造体 (およびそれに接続されたイベント マネージャー) に対応する 1 つだけのイベント ベースがあります。 、一連のイベントをホストするためにスケジュールによって使用され、オペレーティング システムのプロセス管理と比較できます。もちろん、それはより単純です。イベントが発生すると、event_base は、イベントにバインドされた関数を適切なタイミング (必ずしも直ちにではありません) に呼び出します (いくつかの事前定義されたパラメーターとバインド中に指定されたパラメーターを渡します)。関数が実行された後は、他のイベントのスケジュール。

//event_base を作成します

structevent_base *base =event_base_new();

assert(base != NULL);

event_base 内には、epoll / kqueue などのシステム コールをブロックするループがあります。 、1 つまたはいくつかのイベントが発生するまで、その後これらのイベントを処理します。もちろん、これらのイベントはこのevent_baseにバインドされている必要があります。各イベントは struct イベントに対応し、fd または POSIX セマフォをリッスンできます (ここでは fd についてのみ説明します。その他についてはマニュアルを参照してください)。 structeven は、event_new を使用して作成およびバインドし、event_add を使用して有効にします:

//イベントを作成してバインドします

structevent *listen_event;

//パラメータ:event_base、listen fd、イベントのタイプと属性、バインドコールバック関数、コールバック関数に与えられるパラメータ

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

//パラメータ:event, timeout (struct timeval Type ) EV_TIMEOUT : タイムアウト

(b) EV_READ: ネットワークバッファにデータがある限り、コールバック関数がトリガーされます

(c) EV_WRITE: ネットワークバッファに送信されたデータが書き込まれている限り、コールバック関数はトリガーされますトリガーされる

(d) EV_SIGNAL: POSIX セマフォ、マニュアルを参照

(e) EV_PERSIST: この属性が指定されていない場合、コールバック関数がトリガーされた後にイベントが削除されます

(f) EV_ET: エッジ -エッジ トリガーをトリガーします。EPOLL_ET を参照してください

次に、発生するイベントの処理を開始できるように、event_base ループを開始する必要があります。イベントベースのディスパッチはループ内で開始され、ループは注意が必要なイベントがなくなるか、event_loopbreak() /event_loopexit() 関数が検出されるまで継続します。

//イベントループを開始します

event_base_dispatch(base);

次に、イベントにバインドされたコールバック関数 callback_func に注目します。それに渡されるのは、ソケット fd、イベントタイプと属性 bit_field、およびevents_new に渡される 最後のパラメータ (上記の行に戻って確認すると、event_base が渡されます。実際には、構造体を割り当て、関連するすべてのデータを入れて、それを取得できるevent_new にスローすることが重要です。ここ)。そのプロトタイプは次のとおりです:

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

サーバーの場合、上記のプロセスは次のように大まかに組み合わされます:

1. bind()、listen()、nonblocking の設定 (fcntl 設定は POSIX システムで使用でき、Windows を設定する必要はありません。

実際には libevent は統合パッケージ evutil_make_socket_nonblocking を提供します)

2.event_base を作成します

3.イベント、ソケットをevent_baseにホストし、監視するイベントタイプを指定し、対応するコールバック関数(およびそれに与える必要があるパラメータ)をバインドします

    。对于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:回复内容]

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)