【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器

服务器比较简陋,为了学习poll的使用,只向客户端回写一条html语句。启动服务器后,浏览器发起请求,服务端向浏览器写回html,响应字符串,然后可以看到,浏览器解析并显示 Hello Poll!.

启动服务端:

用浏览器访问:

浏览器解析出字符串:

完整代码:

  1 #include <stdio.h>  
  2 #include <stdlib.h>  
  3 #include <unistd.h>  
  4 #include <netinet/in.h>  
  5 #include <arpa/inet.h>  
  6 #include <unistd.h>  
  7 #include <string.h>   
  8 #include <poll.h>  
  9 #include <sys/socket.h>  
 10 #include <sys/types.h>  
 11 #include <sys/stat.h>  
 12   
 13   
 14 #define POLLFD_SIZE 1024 /* struct pollfd 结构体数组最大上限 */  
 15   
 16   
 17   
 18 /* 关心描述符集事件数组*/  
 19 struct pollfd array_pollfd[POLLFD_SIZE];  
 20   
 21 /* 结构体成员详情 
 22 struct pollfd  
 23 { 
 24     int fd;        // 关心的描述符 
 25     short events;  // 关心的事件 
 26     short revents; // 发生的事件 
 27 }; 
 28 */  
 29   
 30 /* 获取一个监听连接的sockfd */  
 31 int run_getsockfd(const char*ip, int port);  
 32   
 33 /* 执行poll检测 */  
 34 void run_poll(int listen_sockfd);  
 35   
 36 /* 响应客户端的连接,并添加新的描述符到关心事件中 */  
 37 void run_accept(int listen_sock);  
 38   
 39 /* 当与客户端连接的描述符有事件就绪时,做出响应 */  
 40 void run_action( int index);  
 41   
 42 int main(int argc, char **argv)  
 43 {  
 44     if(argc != 3)  
 45     {  
 46         printf("usage: [server_ip] [server_port]");  
 47         return 1;  
 48     }  
 49   
 50     int listen_sockfd = run_getsockfd(argv[1], atoi(argv[2]));  
 51   
 52     run_poll(listen_sockfd);  
 53   
 54     return 0;  
 55 }  
 56   
 57   
 58 /* 调用poll 并检测返回事件 */  
 59 void run_poll(int listen_sockfd)  
 60 {  
 61     /* 将负责监听连接的sockfd注册事件 */  
 62     array_pollfd[0].fd = listen_sockfd;  
 63     array_pollfd[0].events = POLLIN;  
 64   
 65     /* 初始化数组中的描述符 */  
 66     int idx_init = 1;  
 67     for(; idx_init < POLLFD_SIZE; ++idx_init)  
 68     {  
 69         array_pollfd[idx_init].fd = -1;  
 70     }  
 71     int timeout = 1000; /* 设定一秒后超时 */  
 72   
 73   
 74     while(1)  
 75     {  
 76         int ret_poll = poll(array_pollfd,  POLLFD_SIZE, timeout);  
 77   
 78         if(ret_poll == 0)        /* 超时    */  
 79             printf("timeout
");  
 80         else if(ret_poll < 0)    /* 执行出错*/  
 81             perror("poll()");  
 82         else  
 83         {/* 有关心的事件就绪  */  
 84   
 85             /* 遍历数组,轮询检测poll的结果  */  
 86             int idx_check = 0;  
 87             for(idx_check = 0; idx_check < POLLFD_SIZE; ++idx_check)  
 88             {  
 89                 if(idx_check == 0 && array_pollfd[0].revents & POLLIN)  
 90                 {/* listen_sockfd 读事件就绪 */  
 91                     run_accept(listen_sockfd);   
 92                 }  
 93                 else if(idx_check != 0)  
 94                 {/* 与客户端连接的sockfd 有事件就绪 */  
 95                     run_action(idx_check);  
 96                 }  
 97             }  
 98         }  
 99     } // end while 1  
100 }  
101   
102   
103 /* 当与客户端连接的描述符有事件就绪时,做出响应 */  
104 void run_action( int index)  
105 {  
106     if(array_pollfd[index].revents & POLLIN)  
107     {/* 客户端读事件发生 */  
108         char buf[1024];          /* 存储从客户端读来的消息 */   
109         memset(buf, 0, sizeof(buf));  
110         ssize_t s = read(array_pollfd[index].fd, buf, sizeof(buf)-1);  
111         if(s > 0)  
112         {  
113             buf[s-1] = '';  
114             printf("client say$ %s 
", buf);  
115             array_pollfd[index].events = POLLOUT;  
116         }  
117         else if( s <= 0)  
118         {  
119             printf("client quit!
");  
120             close(array_pollfd[index].fd);  
121             array_pollfd[index].fd = -1;  
122         }  
123   
124     }  
125     else if (array_pollfd[index].revents & POLLOUT)  
126     {/* 客户端写事件发生 */  
127         /* 使用浏览器测试,写回到客户端,浏览器会解析字符串,显示 Hellp Epoll! */  
128         const char* msg = "HTTP/1.1 200 OK

<html><br/><h1>Hello poll!</h1></html>";  
129         write(array_pollfd[index].fd, msg, strlen(msg));      
130         close(array_pollfd[index].fd);  
131         array_pollfd[index].fd = -1;  
132     }  
133 }  
134   
135   
136 /* 响应客户端的连接,并添加新的描述符到关心事件中 */  
137 void run_accept(int listen_sock)  
138 {  
139     struct sockaddr_in cliaddr;  
140     socklen_t clilen = sizeof(cliaddr);  
141   
142     int new_sock = accept(listen_sock, (struct sockaddr*)&cliaddr, &clilen);  
143     if( new_sock < 0)  
144     {  
145         perror("accept");  
146         return ;  
147     }  
148   
149     printf("与客户端连接成功: ip %s port %d 
", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));  
150     /* 将新socket描述符添加到数组中 */  
151     int idx_find = 1;  
152     for(; idx_find < POLLFD_SIZE; ++idx_find)  
153     {  
154         if(array_pollfd[idx_find].fd < 0)  
155         {  
156             array_pollfd[idx_find].fd = new_sock;  
157             array_pollfd[idx_find].events = POLLIN ;  
158             break;  
159         }  
160     }     
161     if(idx_find == POLLFD_SIZE)  
162     {  
163         perror("连接超出最大限度,add array_pollfd[]");  
164         return;  
165     }  
166   
167 }  
168   
169 /* 获取一个监听socket */  
170 int run_getsockfd(const char* ip, int port)  
171 {  
172     int sock = socket(AF_INET, SOCK_STREAM, 0);  
173     if( sock < 0){  
174         perror("socket()");  
175         exit(1);  
176     }  
177   
178     int opt = 1;  
179     setsockopt(sock , SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
180   
181     struct sockaddr_in server;  
182     bzero(&server, sizeof(server));  
183     server.sin_addr.s_addr = inet_addr(ip);  
184     server.sin_port = htons(port);  
185     server.sin_family = AF_INET;  
186   
187     if(bind(sock, (struct sockaddr *)&server, sizeof(server) ) < 0){  
188         perror("bind()");  
189         exit(2);  
190     }  
191   
192     if(listen(sock, 5) < 0){  
193         perror("listen()");  
194         exit(3);  
195     }  
196   
197     return sock;  
198 }  
原文地址:https://www.cnblogs.com/jiangzhaowei/p/8831108.html