> php教程 > php手册 > 본문

Windows 환경에서 간단한 libevent 서버 구현

WBOY
풀어 주다: 2016-10-19 10:19:31
원래의
1323명이 탐색했습니다.

저는 Windows 시스템을 사용하고 있기 때문에 최근 Libevent를 배우고 있습니다. 안타깝게도 VS에서 참조할 수 있는 프로그램이 거의 없습니다. 여러 블로그 글을 참고한 후. 나는 간단한 Libevent Server 프로그램을 탐색하고 작성했습니다. 또한 온라인에서 간단한 클라이언트 프로그램을 찾아 코드를 성공적으로 테스트했습니다. 오늘 여기에 메모를 해보세요.

Libevent는 정말 유용한 기능이고, 앞으로는 Windows에서 Libevent의 멀티스레딩을 구현해보겠습니다. 오늘은 참고용으로 제가 만든 것을 포스팅하겠습니다. vs2015를 컴파일하고 전달했습니다.

기본적으로 단일 스레드입니다(필요한 경우 다중 스레드로 구성 가능). 각 스레드에는 struct event_base 구조에 해당하는 단 하나의 이벤트 베이스가 있습니다. 연결된 이벤트 관리자)는 일정에 따라 일련의 이벤트를 호스트하는 데 사용됩니다. 이는 물론 운영 체제의 프로세스 관리와 비교할 수 있으며 더 간단합니다. 이벤트가 발생하면 event_base는 적절한 시간(즉시 필요하지는 않음)에 이벤트에 바인딩된 함수를 호출합니다(사전 정의된 일부 매개 변수와 바인딩 시 지정된 매개 변수 전달). 함수가 실행된 후 다음으로 돌아갑니다. 다른 행사 일정.

//event_base 만들기

struct event_base *base = event_base_new();

assert(base != NULL);

event_base 내부에는 하나 또는 일부 이벤트가 발생할 때까지 epoll/kqueue와 같은 시스템 호출을 차단한 후 이러한 이벤트를 처리하는 루프가 있습니다. 물론 이러한 이벤트는 이 event_base에 바인딩되어야 합니다. 각 이벤트는 fd 또는 POSIX 세마포어를 수신할 수 있는 구조체 이벤트에 해당합니다(여기에서는 fd만 설명합니다. 다른 이벤트는 매뉴얼을 참조하세요). struct 이벤트는 event_new를 사용하여 생성 및 바인딩하고, event_add를 사용하여 활성화합니다.

//이벤트 생성 및 바인딩

struct event *listen_event;

//매개변수: event_base, 모니터링된 fd, 이벤트 유형 및 속성, 바운드 콜백 함수, 콜백 함수에 대한 매개변수

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

//매개변수: 이벤트, 시간 초과 시간(struct timeval *type, NULL은 시간 초과 설정이 없음을 의미)

event_add(listen_event, NULL) ;

참고: libevent에서 지원하는 이벤트 및 속성에는 다음이 포함됩니다(비트 필드를 사용하여 구현되었으므로 |를 사용하여 결합)

(a) EV_TIMEOUT: timeout

(b) EV_READ: 네트워크 버퍼에 데이터가 있는 한 콜백 함수가 동작합니다

(c) EV_WRITE: 네트워크 버퍼에 삽입된 데이터가 쓰여지는 동안 콜백 함수가 동작합니다. 트리거

(d) EV_SIGNAL: POSIX 세마포어, 매뉴얼 참조

(e) EV_PERSIST: 이 속성을 지정하지 않으면 콜백 함수가 트리거된 후 이벤트가 삭제됩니다

(f) EV_ET: Edge - 에지 트리거, EPOLL_ET

를 참조한 다음 발생하는 이벤트 처리를 시작할 수 있도록 event_base 루프를 시작해야 합니다. 이벤트 베이스 디스패치가 루프에서 시작되고 주의가 필요한 이벤트가 더 이상 없거나 event_loopbreak() / event_loopexit() 함수가 발생할 때까지 루프가 계속됩니다.

//이벤트 루프 시작

event_base_dispatch(base);

다음으로 이벤트에 바인딩된 콜백 함수 callback_func에 집중합니다. 여기에 전달되는 것은 소켓입니다. fd, 이벤트 유형 및 속성 bit_field, event_new에 전달된 마지막 매개변수(위의 줄로 이동하여 검토하세요. event_base가 전달되었습니다. 실제로는 구조를 할당하고 관련 데이터를 모두 넣은 다음 event_new에 던지면 여기에서 얻을 수 있습니다.) 프로토타입은 다음과 같습니다.

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

서버의 경우 위 프로세스는 아마도 다음과 같습니다. :

1. 리스너 = 소켓(), 바인드(), 청취(), 비차단 설정(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:回复内容]

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 추천
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿