I/O復用的 select poll和epoll的簡單實現

http://www.cnblogs.com/wj9012/p/3876734.html

一個tcp的客戶端服務器程序

服務器端不變,客戶端通過I/O復用輪詢鍵盤輸入與socket輸入(接收客戶端的信息)

服務器端:

復制代碼
 1 /*服務器:
 2 1.客戶端關閉后,服務器再向客戶端發送信息,第一次會收到一個RST復位報文,第二次會收到SIGPIPE信號,導致服務器關閉,必須對這個信號進行處理:
 3     1.在服務器對read返回值為0的情況進行處理,不向客戶端發送信息
 4     2.signal函數: signal(SIGPIPE, handle) 或者直接忽略signal(SIGPIPE, SIG_IGN)
 5 */
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 #include <unistd.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/socket.h>
14 #include <arpa/inet.h>
15 #include <signal.h>
16 
17 #define ERR_EXIT(m) \
18     do { \
19         perror(m);\
20         exit(EXIT_FAILURE);\
21     }while(0)//宏定義的錯誤處理
22 
23 static void do_service(int fd)
24 {
25     char recvbuf[1024] = {0};
26     int ret;
27     while(1)
28     {
29         memset(recvbuf, 0, sizeof recvbuf);
30         ret = read(&rt, recvbuf, 1024);
31         sleep(3);
32         if(ret == 0)//關閉客戶端時會導致ret?== 0
33             printf("no message\n");
34         else if(ret == -1)
35         {
36             if(errno == EINTR)
37                 continue;
38             return ;
39         }   
40         rio_writen(fd, recvbuf, 5);
41     }
42 }
43 
44 void handle(int signum)//SIGPIPE信號處理函數
45 {
46 printf("hello\n");
47 }
48 
49 int main(int argc, const char *argv[])
50 {
51     if(signal(SIGPIPE, handle) == SIG_ERR)
52         ERR_EXIT("signal");    
53 
54     int listenfd = socket(AF_INET, SOCK_STREAM, 0);//準備一個socketfd
55     if(listenfd == -1 )
56         ERR_EXIT("listen");
57 
58     int on = 1;
59     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt設置端口復用
60     {
61         close(listenfd);
62         ERR_EXIT("setsockopt");
63     }
64 
65     struct sockaddr_in seraddr;
66     seraddr.sin_family = AF_INET;
67     seraddr.sin_port = htons(8888);
68     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
69     socklen_t len = sizeof(seraddr);
70     if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//監聽socket端口,
71     {
72         close(listenfd);
73         ERR_EXIT("bind");
74     }
75?//上述過程可以封裝到起來,不用全部寫入main
76     if(listen(listenfd, 6) == -1)
77     {
78         close(listenfd);
79         ERR_EXIT("listen");
80     }
81 
82     struct sockaddr_in cliaddr;
83     bzero(&cliaddr, sizeof(cliaddr));
84     socklen_t cli_len = sizeof cliaddr;
85 
86     int clientfd;
87     clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
88 
89     if(clientfd == -1)  
90     {
91         close(listenfd);
92         ERR_EXIT("accept");
93     }
94     do_service(clientfd);
95 
96     close(clientfd);
97     close(listenfd);
98     return 0;
99 }
復制代碼

?

1.select客戶端:?

復制代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <sys/socket.h>
 7 #include <netinet/in.h>
 8 #include <arpa/inet.h>
 9 #include <sys/select.h>
10 
11 #define ERR_EXIT(m) \
12     do { \
13         perror(m);\
14         exit(EXIT_FAILURE);\
15     }while(0)
16 
17 static void do_client(int fd)
18 {
19     char recvbuf[MAXLINE + 1] = {0};
20     char sendbuf[MAXLINE + 1] = {0};
21 
22     fd_set reade, ready;//將ready集合放入select輪詢,每次輪詢前將reade賦值給ready
23     FD_ZERO(&reade);//清空reade集合
24     int fd_stdin = fileno(stdin);
25     FD_SET(fd_stdin, &reade);
26     FD_SET(fd, &reade);//將需要監聽的文件描述符加入集合
27     int fd_max = (fd_stdin > fd) ? fd_stdin : fd;//select輪詢的最大文件描述符
28 
29     int ret;
30     while(1)
31     {
32         ready = reade;
33         ret = select( fd_max+1, &ready, NULL, NULL, NULL);//輪詢,最后一個參數struct *timeval設置為NULL表示即時輪詢(每次輪詢間沒時間間隔)
34         if(ret < 0)
35         {
36             if(errno == EINTR)
37                 continue;
38             ERR_EXIT("select");
39         }else if(ret ==  0)//監聽的文件描述符狀態未發生變化
40         {
41             continue;
42         }
43 
44         if(FD_ISSET(fd_stdin, &ready))
45         {
46             if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
47             {
48                 shutdown(fd, SHUT_WR);//關閉fd的寫端,close關閉時關閉整個描述符,若再寫的話會導致write返回-1,系統會向客戶端進程發送一個SIGPIPE信號,
導致關閉,可以對信號進行signal(SIGPIPE, SIG_IGN)處理,也可將fd_stdin從監聽隊列刪除
49 // close(fd); 50 exit(EXIT_SUCCESS); 51 ?//break;
52 }else 53 write(fd, sendbuf, strlen(sendbuf)); 54 } 55 56 if(FD_ISSET(fd, &ready)) 57 { 58 int nread = read(fd, recvbuf, MAXLINE); 59 if(nread < 0) 60 ERR_EXIT("read"); 61 if(nread == 0)//如果沒接收到消息,打印關閉描述符,退出循環 62 { 63 fprintf(stdout, "fd close\n"); 64 break; 65 } 66 fprintf(stdout, "receive:%s\n", recvbuf);//注意要刷新 67 } 68 memset(recvbuf, 0, sizeof recvbuf); 69 memset(sendbuf, 0, sizeof sendbuf); 70 } 71 } 72 73 int main(int argc, const char *argv[]) 74 { 75 int fd = socket(AF_INET, SOCK_STREAM, 0); 76 if(fd < 0) 77 ERR_EXIT("socket"); 78 79 struct sockaddr_in cliaddr; 80 cliaddr.sin_family = AF_INET; 81 cliaddr.sin_port = htons(8888); 82 cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 83 socklen_t len = sizeof cliaddr; 84 85 int ret ; 86 if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1) 87 { 88 close(fd); 89 ERR_EXIT("connect"); 90 } 91 do_client(fd); 92 close(fd); 93 printf("123\n"); 94 return 0; 95 }
復制代碼


2.poll客戶端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static?void?do_client(int?fd)
{
????char?recvbuf[MAXLINE + 1] = {0};
????char?sendbuf[MAXLINE + 1] = {0};
?????struct?pollfd pfd[2];//struct pollfd數組,里面存放監聽描述符相關信息,供poll輪詢使用
?????pfd[0].fd = fileno(stdin);
?????pfd[0].events = POLLIN;
?????pfd[1].fd = fd;
?????pfd[1].events = POLLIN;
?????int?ret;
????while(1)
????{
????????ret = poll( pfd, 2, -1);//輪詢,-1和select中的時間結構體指針為NULL的效果一樣
????????if(ret < 0)
????????{
????????????if(errno?== EINTR)
????????????????continue;
????????????ERR_EXIT("select");
????????}else?if(ret ==? 0)
????????{
????????????continue;
????????}
????????if(pfd[0].revents & POLLIN)
????????{
????????????if(fgets(sendbuf,?sizeof(sendbuf), stdin) == NULL)
????????????{
????????????????shutdown(fd, SHUT_WR);//關閉fd的寫端
????????????????pfd[0].fd = -1;
????????????}else
????????????????write(fd, sendbuf,?strlen(sendbuf));??????????
????????}
????????if(pfd[1].revents & POLLIN)
????????{
????????????int?nread = read(fd, recvbuf, MAXLINE);
????????????if(nread < 0)
????????????????ERR_EXIT("read");
????????????if(nread == 0)//如果沒接收到消息,打印關閉描述符,退出循環
????????????{
????????????????fprintf(stdout,?"fd close\n");
????????????????break;
????????????}
????????????fprintf(stdout,?"receive:%s\n", recvbuf);//注意要刷新
????????}
????????memset(recvbuf, 0,?sizeof?recvbuf);
????????memset(sendbuf, 0,?sizeof?sendbuf);
????}
}

//其余部分和select中一樣

3.epoll客戶端?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static?void?do_client(int?fd)
{
????char?recvbuf[MAXLINE + 1] = {0};
????char?sendbuf[MAXLINE + 1] = {0};
????int?epollfd = epoll_create(2);//返回值也是一個文件描述符,記得要關閉,不然會導致fd耗盡
????if(epollfd == -1)
????????ERR_EXIT("epoll_create");
????struct?epoll_event events[2];
????struct?epoll_event ev;
????int?ret;
????ev.data.fd = fd;
????ev.events = EPOLLIN;
????ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);//注冊fd
????if(ret == -1)
????????ERR_EXIT("epoll_ctl");
????ev.data.fd = fileno(stdin);
????ev.events = EPOLLIN;
????ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);//注冊fd
????if(ret == -1)
????????ERR_EXIT("epoll_ctl");
????int?nready;
????while(1)
????{
????????nready = epoll_wait(epollfd, events, 2, -1);
????????if(nready < 0)
????????{
????????????if(errno?== EINTR)
????????????????continue;
????????????ERR_EXIT("select");
????????}else?if(nready? ==? 0)
????????{
????????????continue;
????????}
????????int?i;
????????for(i = 0; i < nready; i++)???
????????{
????????????int?nfd = events[i].data.fd;
????????????if(nfd == STDIN_FILENO)
????????????{
????????????????if(fgets(sendbuf, 1024, stdin) == NULL)
????????????????{
????????????????????shutdown(fd, SHUT_WR);
????????????????????struct?epoll_event ee;
????????????????????ee.data.fd = STDIN_FILENO;
????????????????????if(epoll_ctl(epollfd, EPOLL_CTL_DEL, STDIN_FILENO, &ee) == -1)
????????????????????????ERR_EXIT("epoll_ctl");
????????????????}else
????????????????????write(fd, sendbuf,?strlen(sendbuf));
????????????}
????????????if(nfd == fd)
????????????{
????????????????int?ret = read(fd, recvbuf, 1024);
????????????????if(ret == -1)
????????????????????ERR_EXIT("readline");
????????????????else?if(ret == -1)
????????????????{
????????????????????close(fd);
????????????????????printf("fd close\n");
????????????????????exit(EXIT_SUCCESS);
????????????????}
????????????????printf("recv data :%s\n", recvbuf);
????????????}
????????}
????}
????memset(recvbuf, 0,?sizeof?recvbuf);
????memset(sendbuf, 0,?sizeof?sendbuf);<br>? }<br>close(epollfd);
}

?


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383954.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383954.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383954.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

netstat 相關命令解析

1.列出所有的端口 netstat -a 列出TCP協議的端口 netstat -at UDP協議的端口 netstat -au 2.列出處于監聽狀態的socket netstat -l 列出監聽的TCP端口 netstat -lt 列出監聽的UDP端口 netstat -lu 列出監聽的UNIX端口 netstat -lx 3.列出協議的統計信息 nestat …

UVa10791

我們可以先用唯一分解定理將這個數字分解成素因子冪的乘積&#xff0c;為了得到最小的和&#xff0c;我們可以發現&#xff1a;每個 素因子的冪單獨分開的和是最小的。 先說明每個素因子都是以出現的最大的次數出現。因為最小公倍數一定&#xff0c;因此至少有一個數字的這個素…

TCP相關代碼

TCP 基礎代碼 //tcp_server.c #include<stdio.h> #include<error.h> #include<sys/types.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include <arpa/inet.h> #include<st…

UVa1635

我們很容易發現最后每一項的系數就是二項式展開&#xff0c;余數和m沒有關系就意味著可以被m整除&#xff0c;因此我們就需要求出每一個二項式的系數。但是數據實在太大我們根據唯一分解定理將m和系數都進行分解&#xff0c;然后比較因子的冪。 二項式的計算我們可以根據楊輝三…

幾種并發服務器模型的實現:多線程,多進程,select,poll,epoll

http://www.cnblogs.com/wj9012/p/3879605.html 客戶端使用select模型&#xff1a; 1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 #include <errno.h>5 #include <sys/types.h>6 #include <sys/socket.h>7 #include …

哈希表1

1. 初始化 void HashInit(HashTable* ht, HashFunc func) {if(ht NULL || func NULL){return;}ht -> size 0;ht -> func func;int i 0;for(; i < HashMaxSize; i){ht -> data[i].state Empty;} } 2. 哈希表的銷毀 void HashDestroy(HashTable* ht) {if(ht…

UVa10820

實質上就是求歐拉函數值 書上有個板子挺好&#xff0c;也不難理解。 #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cctype> #include<queue> #include<set>using namespace std;typedef l…

linux socket 編程(C語言)

https://www.cnblogs.com/x_wukong/p/4541010.html 最近看了一些網絡編程的書籍&#xff0c;一直以來總感覺網絡編程神秘莫測&#xff0c;其實網絡編程入門還是很容易學的&#xff0c;下面這些代碼是我在linux下編寫的&#xff0c;已經運行過了&#xff0c;編譯之后就可以運行了…

哈希表2

哈希表的初始化 void HashInit(HashTable* ht, HashFunc func) {if(ht NULL){return;}ht -> size 0;ht -> func func;size_t i 0;for(; i < MaxSize; i){ht -> data[i] NULL;} } 哈希表的結點創建 HashElem* CreateHashElemNode(KeyType key, ValueType va…

位圖

相關數據結構 typedef uint64_t BitmapType;#define BITMAPMAXSIZE 1000 //位圖所能容納的位數typedef struct Bitmap {uint64_t* data;uint64_t capacity; }Bitmap; 初始化 void BitmapInit(Bitmap* bm, uint64_t capacity) {if(bm NULL){return;}//當capacity 100, 2個元…

C++中的inline用法

https://www.cnblogs.com/fnlingnzb-learner/p/6423917.html 1. 引入inline關鍵字的原因 在c/c中&#xff0c;為了解決一些頻繁調用的小函數大量消耗棧空間&#xff08;棧內存&#xff09;的問題&#xff0c;特別的引入了inline修飾符&#xff0c;表示為內聯函數。 棧空間就是指…

UVa1262

算是一個模擬吧 #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cctype> #include<queue> #include<set> #include<vector>using namespace std;typedef long long ll; const int INF…

一個Linux下C線程池的實現

http://blog.csdn.net/zouxinfox/article/details/3560891 什么時候需要創建線程池呢&#xff1f;簡單的說&#xff0c;如果一個應用需要頻繁的創建和銷毀線程&#xff0c;而任務執行的時間又非常短&#xff0c;這樣線程創建和銷毀的帶來的開銷就不容忽視&#xff0c;這時也是線…

Gym100917 A - Abstract Picture

模擬賽的時候看這道題沒有什么頭緒&#xff0c;當時有點暈&#xff0c;感冒還沒有好&#xff0c;回來以后瞟了一眼題解就明白了&#xff0c;自己實現了一下&#xff0c;也沒有很復雜。大概的思路就像拓撲排序一樣&#xff0c;需要理解因為涂的是有順序的&#xff0c;所以我們總…

linux進程通信---幾個發送信號的函數(kill,raise,alarm,pause)

http://blog.csdn.net/zzyoucan/article/details/9235685 信號&#xff1a;信號是unix中最古老的進程通信的一種方式&#xff0c;他是軟件層次上對中斷機制的模擬&#xff0c;是一種異步通信方式&#xff0c;信號可以實現用戶空間進程和內核空間進程的交互&#xff0c;內核進程…

數據庫以及表的基本操作

一.數據庫的操作 create database[if not exists]數據庫名; 創建一個名字為company2的使用utf8忽略大小寫的數據庫 create database company charsetutf8 collate utf8_general_ci; 創建一個數據庫區分大小寫 create database company1 charsetutf8 collate utf8_general_bin;…

linux 網絡編程:使用兩線程實現socket同時收發數據

http://blog.csdn.net/li_wen01/article/details/52665505 工作中最近有使用到socket 向客戶端同時發送和接收數據&#xff0c;因為是嵌入式linux設備&#xff0c;且要求只能同時一個客戶端連接該端口。考慮到節省系統資源&#xff0c;只創建了兩個線程分別實現服務端的收發數據…

CF Gym102059 H. Fractions

題目要求找到給定區間的化簡后分子分母的和小于1000的數字的個數 我的想法是先找到所有的滿足要求的最簡分數(總數不超過1e6,而且遠小于),然后對詢問查找每個最簡分數出現的次數. #include<cstdio> #include<cstring> #include<algorithm> #include<cli…

C語言calloc()函數:分配內存空間并初始化

http://c.biancheng.net/cpp/html/134.html 頭文件&#xff1a;#include <stdlib.h> calloc() 函數用來動態地分配內存空間并初始化為 0&#xff0c;其原型為&#xff1a; void* calloc (size_t num, size_t size); calloc() 在內存中動態地分配 num 個長度為 siz…

CF Gym100917 C

要找到和為給定值的所有的等比數列. 1肯定是要特判一下. 我的想法是先找到所有等比為1的,等比為1就是將這個數分為相同的一些數,總共就是這個數的所有約數個數減一(有一個約數為1,題目要求至少分成兩個數). 對于其他的等比不為1 的,用等比數列的求和公式暴力一發就行了. #i…