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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux网络编程(三)select、poll和epoll

發布時間:2023/11/30 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux网络编程(三)select、poll和epoll 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

linux網絡編程(三)select、poll和epoll

  • 一、為什么會有多路I/O轉接服務器?
  • 二、select
  • 三、poll
  • 三、epoll

一、為什么會有多路I/O轉接服務器?

為什么會有多路I/O轉接服務器呢?在學這個之前,我們同使用的是多線程或者多進程的方式來連接服務器,那么cpu將會占用大量資源來處理。那么我們試想一下,如果有幾千臺客戶端來連接呢,那么就要創建幾千個進程或者線程。有沒有一種不怎么占用cpu資源的方式呢。這時候多路I/O轉接服務器的這種機制就很符合這種情況。操作系統會用內核來監聽文件,而不再用進程或者線程來監聽,相當于請了一個“幫手”,當這些客戶端請求的時候,這個“幫手”會幫你處理好,再反饋給程序,此時調用accept就無需等待了,內核已經幫你弄好了

二、select

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

nfds: 監控的文件描述符集里最大文件描述符加1,因為此參數會告訴內核檢測前多少個文件描述符的狀態 readfds: 監控有讀數據到達文件描述符集合,傳入傳出參數 writefds: 監控寫數據到達文件描述符集合,傳入傳出參數 exceptfds: 監控異常發生達文件描述符集合,如帶外數據到達異常,傳入傳出參數 timeout: 定時阻塞監控時間,3種情況1.NULL,永遠等下去2.設置timeval,等待固定時間3.設置timeval里時間均為0,檢查描述字后立即返回,輪詢 struct timeval {long tv_sec; /* seconds */long tv_usec; /* microseconds */ }; void FD_CLR(int fd, fd_set *set); //把文件描述符集合里fd清0 int FD_ISSET(int fd, fd_set *set); //測試文件描述符集合里fd是否置1 void FD_SET(int fd, fd_set *set); //把文件描述符集合里fd位置1 void FD_ZERO(fd_set *set); //把文件描述符集合里所有位清0 #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <sys/select.h> #include <string.h> #include <arpa/inet.h> #include <ctype.h> #include "wrap.h" #define SERV_PORT 6666 #define CONNECT 30int main() {int listen_fd,ret,maxfd, nready,con_fd,i,sockfd,n;int client[FD_SETSIZE];char buf[BUFSIZ] = { 0 };//創建套接字,已經封裝好錯誤處理listen_fd = Socket(AF_INET,SOCK_STREAM,0);//綁定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd,sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd,(struct sockaddr*)&sockadd,sizeof(struct sockaddr_in));//設置同時3次握手的次數Listen(listen_fd, CONNECT);//初始化最大數量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd,&allset);FD_SET(listen_fd, &rset);int maxi = -1; // client[]的下標//初始化for (i = 0; i < FD_SETSIZE; i++)client[i] = -1; /* 用-1初始化client[] */while (1){rset = allset;nready = select(maxfd + 1, &rset, NULL, NULL, NULL);if (FD_ISSET(listen_fd, &rset)) //判斷是否是新的客戶端連接{printf("new client online......\n");con_fd=Accept(listen_fd, (struct sockaddr*)&clie_add,&len);FD_SET(con_fd, &allset);for (i = 0; i < FD_SETSIZE; i++){if (client[i] < 0){client[i] = con_fd;break;}}if (i > maxi){maxi = i;}//最大值更新if (con_fd > maxfd){maxfd = con_fd;}if (--nready == 0)continue;}else //讀數據{for (i = 0; i <= maxi; i++){if ((sockfd = client[i]) < 0){continue;}if (FD_ISSET(sockfd, &rset)){//讀數據if ((n = Read(sockfd, buf, sizeof(buf))) == 0){Close(sockfd); /* 當client關閉鏈接時,服務器端也關閉對應鏈接 */FD_CLR(sockfd, &allset); /* 解除select監控此文件描述符 */client[i] = -1;}else{int j;for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Write(sockfd, buf, n);}if (--nready == 0){break;}}}}}return 0; }

三、poll

poll是select進階的函數,
int poll(struct pollfd fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; / 文件描述符 /
short events; / 監控的事件 /
short revents; / 監控事件中滿足條件返回的事件 */
};

#include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <sys/select.h> #include <string.h> #include <arpa/inet.h> #include <ctype.h> #include "wrap.h" #include <poll.h> #define SERV_PORT 6666 #define CONNECT 30 #define OPEN_MAX 1024 int main() {int listen_fd, ret, maxfd, nready, con_fd, i, sockfd, n;struct pollfd client[OPEN_MAX];char buf[BUFSIZ] = { 0 };//創建套接字,已經封裝好錯誤處理listen_fd = Socket(AF_INET, SOCK_STREAM, 0);//綁定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd, sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd, (struct sockaddr*)&sockadd, sizeof(struct sockaddr_in));//設置同時3次握手的次數Listen(listen_fd, CONNECT);//初始化最大數量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd, &allset);FD_SET(listen_fd, &rset);int maxi = 0; // client[]的下標//初始化for (i = 0; i < OPEN_MAX; i++)client[i].fd = -1; /* 用-1初始化client[] */while (1){nready = poll(client, maxi+1,-1);if (client[0].revents & POLLIN) //判斷是否是新的客戶端連接{printf("new client online......\n");con_fd = Accept(listen_fd, (struct sockaddr*)&clie_add, &len);FD_SET(con_fd, &allset);for (i = 1; i < FD_SETSIZE; i++){if (client[i].fd < 0){client[i].fd = con_fd;break;}}client[i].events = POLLIN;if (i > maxi){maxi = i;}//最大值更新if (con_fd > maxfd){maxfd = con_fd;}if (--nready == 0)continue;}else //讀數據{for (i = 1; i <= maxi; i++){if ((sockfd = client[i].fd) < 0){continue;}if (client[i].revents & POLLIN){//讀數據if ((n = Read(sockfd, buf, sizeof(buf))) < 0){if (errno = ECONNRESET) //收到RST標志{printf("client[%d] aborted connection\n",i);Close(sockfd);client[i].fd = -1;//不監聽該文件描述符}}if ((n = Read(sockfd, buf, sizeof(buf))) == 0){Close(sockfd); /* 當client關閉鏈接時,服務器端也關閉對應鏈接 */FD_CLR(sockfd, &allset); /* 解除select監控此文件描述符 */client[i].fd = -1;}else{int j;for (j = 0; j < n; j++)buf[j] = toupper(buf[j]);Write(sockfd, buf, n);}if (--nready == 0){break;}}}}}return 0; }

三、epoll

epoll是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率,因為它會復用文件描述符集合來傳遞結果而不用迫使開發者每次等待事件之前都必須重新準備要被偵聽的文件描述符集合,另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)epfd: 為epoll_creat的句柄op: 表示動作,用3個宏來表示:EPOLL_CTL_ADD (注冊新的fd到epfd),EPOLL_CTL_MOD (修改已經注冊的fd的監聽事件),EPOLL_CTL_DEL (從epfd刪除一個fd);event: 告訴內核需要監聽的事件struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */};typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;EPOLLIN : 表示對應的文件描述符可以讀(包括對端SOCKET正常關閉)EPOLLOUT: 表示對應的文件描述符可以寫EPOLLPRI: 表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來)EPOLLERR: 表示對應的文件描述符發生錯誤EPOLLHUP: 表示對應的文件描述符被掛斷;EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水平觸發(Level Triggered)而言的EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里 #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <sys/select.h> #include <string.h> #include <arpa/inet.h> #include <ctype.h> #include "wrap.h" #include <poll.h> #include <sys/epoll.h> #define SERV_PORT 6666 #define CONNECT 30 #define OPEN_MAX 1024 int main() {int listen_fd, ret, maxfd, nready, con_fd, i, sockfd, n, efd,res;struct epoll_event tep, ep[OPEN_MAX];struct pollfd client[OPEN_MAX];char buf[BUFSIZ] = { 0 };char str[BUFSIZ] = { 0 };//創建套接字,已經封裝好錯誤處理listen_fd = Socket(AF_INET, SOCK_STREAM, 0);//綁定套接字struct sockaddr_in sockadd;struct sockaddr_in clie_add;socklen_t len;len = sizeof(struct sockaddr_in);bzero(&sockadd, sizeof(struct sockaddr_in));bzero(&clie_add, sizeof(struct sockaddr_in));sockadd.sin_family = AF_INET;sockadd.sin_port = htons(SERV_PORT);sockadd.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listen_fd, (struct sockaddr*)&sockadd, sizeof(struct sockaddr_in));//設置同時3次握手的次數Listen(listen_fd, CONNECT);//初始化最大數量的文件描述符maxfd = listen_fd;fd_set rset;fd_set allset;FD_SET(listen_fd, &allset);FD_SET(listen_fd, &rset);int maxi = 0; // client[]的下標efd = epoll_create(OPEN_MAX);if (efd == -1){perror("epoll error:");exit(1);}tep.events = EPOLLIN;tep.data.fd = listen_fd;res = epoll_ctl(efd,EPOLL_CTL_ADD,listen_fd,&tep);if (res == -1){perror("epoll_ctl error:");exit(1);}while (1){nready = epoll_wait(efd,ep,OPEN_MAX,-1);if (nready == -1){perror("epoll_wait error:");exit(1);}for (i = 0; i < nready; i++){if (!(ep[i].events & EPOLLIN)){continue;}if (ep[i].data.fd == listen_fd){len = sizeof(clie_add);con_fd = Accept(listen_fd, (struct sockaddr*)&clie_add, &len);printf("received from %s at PORT %d \n",inet_ntop(AF_INET, &clie_add.sin_addr.s_addr, str, sizeof(BUFSIZ)),ntohs(clie_add.sin_port));tep.events = EPOLLIN;tep.data.fd = con_fd;res = epoll_ctl(efd, EPOLL_CTL_ADD, con_fd, &tep);if (res == -1){perror("epoll_ctl error:");exit(1);}}else{sockfd = ep[i].data.fd;n = Read(sockfd, buf, BUFSIZ);if (n == 0){res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);if (res == -1){perror("epoll_ctl error:");exit(1);}close(sockfd);printf("client[%d] closed connection\n", sockfd);}else if (n < 0){perror("read n<0 error:");res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);close(sockfd);}else{for (i = 0; i < n; i++){buf[i] = toupper(buf[i]);}Write(STDOUT_FILENO, buf, n);Write(sockfd, buf, n);}}}}return 0; }

總結

以上是生活随笔為你收集整理的linux网络编程(三)select、poll和epoll的全部內容,希望文章能夠幫你解決所遇到的問題。

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