日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

epoll 示例

發布時間:2025/5/22 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 epoll 示例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

epoll 有水平觸發 Level-triggered(LT) 和邊沿觸發 edge-triggered(ET) 兩種模式。

假設有如下過程:

  • 注冊 pipe 文件描述符的讀端(rfd)到 epoll 上
  • 在 pipe 的寫端寫入 2KB 的數據
  • 因為 rfd 文件描述符已經準備好讀,epoll_wait 返回
  • 從 rfd 中讀取 1KB 數據
  • 程序再次運行到 epoll_wait
  • 如果為邊沿觸發(ET)模式,程序將會阻塞在 epoll_wait 上,即使有剩余數據也不會返回,意味著讀不到剩余的 1KB 數據。但如果采用水平觸發(LT),epoll_wait 函數將會再次返回。

    如果采用邊沿觸發(ET)模式,建議:

  • 使用非阻塞文件描述符
  • 調用 read 或 write 直到 errno==EAGAIN
  • 當 close 文件描述符時,該描述符會被自動的從 epoll 監聽集中移除。

    typedef union epoll_data {void *ptr;int fd; uint32_t u32;uint64_t u64; } epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };

    epoll_wait 使用該結構體數組返回就緒的文件描述符集合,這樣就不用遍歷所有的文件描述符。

    源碼

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include <fcntl.h> #include <sys/epoll.h> #include <errno.h>#define MAXEVENTS 64/* 設置 socket 為非阻塞,必須先獲取再設置 */ static int make_socket_non_blocking(int sfd) {int flags, s;flags = fcntl(sfd, F_GETFL, 0);if (flags == -1) {perror ("fcntl");return -1;}flags |= O_NONBLOCK;s = fcntl(sfd, F_SETFL, flags);if (s == -1) {perror("fcntl");return -1;}return 0; }/* 創建并綁定 socket */ static int create_and_bind(char *port) {struct addrinfo hints;struct addrinfo *result, *rp;int s, sfd;memset(&hints, 0, sizeof (struct addrinfo));hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */hints.ai_flags = AI_PASSIVE; /* All interfaces */s = getaddrinfo(NULL, port, &hints, &result);if (s != 0) {fprintf(stderr, "getaddrinfo: %s\n", gai_strerror (s));return -1;}for (rp = result; rp != NULL; rp = rp->ai_next) {/* 創建 socket */sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);if (sfd == -1) {continue;}/* 綁定 socket 到地址 */s = bind(sfd, rp->ai_addr, rp->ai_addrlen);if (s == 0) {/* We managed to bind successfully! */break;}close(sfd);}if (rp == NULL) {fprintf (stderr, "Could not bind\n");return -1;}freeaddrinfo(result);return sfd; }int main(int argc, char *argv[]) {int sfd, efd, s;struct epoll_event event;struct epoll_event *events;if (argc != 2) {fprintf(stderr, "Usage: %s [port]\n", argv[0]);exit(EXIT_FAILURE);}/* 創建并綁定 socket */sfd = create_and_bind(argv[1]);if (sfd == -1) {abort();}/* 設置 socket 為非阻塞 */s = make_socket_non_blocking(sfd);if (s == -1) {abort();}/* 監聽 socket */s = listen(sfd, SOMAXCONN);if (s == -1) {perror("listen");abort();}efd = epoll_create1(0);if (efd == -1) {perror("epoll_create");abort();}event.data.fd = sfd;event.events = EPOLLIN | EPOLLET; // 讀事件 | 邊沿觸發// 添加 sfd(監聽 socket) 到 epolls = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);if (s == -1) {perror("epoll_ctl");abort();}/* Buffer where events are returned */events = calloc(MAXEVENTS, sizeof(event));/* The event loop */while (1) {int n, i;if ((n = epoll_wait(efd, events, MAXEVENTS, -1)) == -1) {perror("epoll_wait");abort();}for (i = 0; i < n; i++) {if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||(!(events[i].events & EPOLLIN))) {/* socket 出錯 | socket 掛起 | socket 讀未準備好* 為什么 socket 讀為準備好也有問題? */fprintf(stderr, "epoll error\n");close(events[i].data.fd);continue;} else if (sfd == events[i].data.fd) {/* 監聽 socket 事件通知,意味著一個或者多個連接到來 */while (1) {struct sockaddr in_addr;socklen_t in_len;int infd;char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];in_len = sizeof(in_addr);infd = accept(sfd, &in_addr, &in_len);if (infd == -1) {if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {/* 所有連接請求都已處理完成 */break;} else {perror ("accept");break;}}s = getnameinfo(&in_addr, in_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),NI_NUMERICHOST | NI_NUMERICSERV);if (s == 0) {printf("Accepted connection on descriptor %d ""(host=%s, port=%s)\n", infd, hbuf, sbuf);}/* 將 socket 轉為非阻塞 */s = make_socket_non_blocking(infd);if (s == -1) {abort();}/* 將新的連接放入 epoll 中 */event.data.fd = infd;event.events = EPOLLIN | EPOLLET; // 讀事件 | 邊沿觸發s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);if (s == -1) {perror ("epoll_ctl");abort();}}continue;} else {int done = 0;/* socket 已經準備好讀。在 EPOLLET 邊沿觸發模式下,必須將緩沖區中的數據都讀取完,* 否則剩下的數據等不到通知 */while (1) {ssize_t count;char buf[1024];count = read(events[i].data.fd, buf, sizeof(buf));if (count == -1) {/* errno == EAGAIN 意味著所有數據都已讀取完成,此時可以跳出循環 */if (errno != EAGAIN) {perror ("read");done = 1;}break;} else if (count == 0) {/* count == 0,意味著對方關閉連接 */done = 1;break;}/* 將緩沖區中的數據寫入 socket */s = write(events[i].data.fd, buf, count);if (s == -1) {perror("write");abort();}}if (done) {printf("Closed connection on descriptor %d\n", events[i].data.fd);/* 將 socket 關閉時,同時也會將該 socket 從 epoll 監聽中移除 */close(events[i].data.fd);}}}}free(events);close(sfd);return EXIT_SUCCESS; }

    參考資料

    1.http://www.oschina.net/translate/how-to-use-epoll-a-complete-example-in-c

    2.man epoll

    轉載于:https://my.oschina.net/lowkey2046/blog/705897

    總結

    以上是生活随笔為你收集整理的epoll 示例的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。