【Linux网络编程】I/O多路转接之 epoll 高性能简洁http服务器模型

来源:互联网 发布:阿里孙正义马云占股份 编辑:程序博客网 时间:2024/05/16 19:46

和之前的select ,poll一样,服务器比较简陋,为了学习模型的基本框架,只向客户端回写一条html语句。启动服务器后,用手机或者电脑浏览器发起请求,服务端向浏览器写回html,响应字符串,然后可以看到,浏览器解析并显示 Hello epoll!.

启动服务器:


浏览器访问,服务端打印出请求报文:


浏览器解析出结果;


完整代码:


#include <stdio.h>#include <assert.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h> #include <sys/socket.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/epoll.h>/* 事件数组最大值 */#define MAX_EVENTS 256/* 每一个客户端在事件集中 events.data.ptr 所指向的数据 */typedef struct fd_buf{int  fd;        /* 保存当前客户端的socket描述符 */char buf[1024]; /* 当前客户端的缓冲区 */}fd_buf_t, *fd_buf_p;/* 为每一个客户端动态分配fd_buf_t 结构体 */static void* new_fd_buf(int fd);/* epoll事件集 */struct epoll_event evts[MAX_EVENTS];/* 获取一个监听连接的sockfd */int run_getsockfd(const char*ip, int port);/* 执行epoll逻辑 (创建,添加监听事件,等待返回)*/void run_epoll(int listen_sockfd);/* 处理客户端事件就绪 */void run_action(int epollfd, int index);/* 处理客户端连接请求,并添加到epoll模型中 */void run_accept(int epollfd, int listen_sockfd);int main(int argc, char** argv){/* 使用格式 ./xxx  服务器ip  服务器端口 */if(argc != 3){printf("usage: [server_ip] [server_port]\n");return 1;}/* 获取监听socketfd  */int listen_sockfd = run_getsockfd(argv[1], atoi(argv[2]));/* 执行epoll 逻辑 */run_epoll(listen_sockfd);return 0;}/* 获取一个监听socket */int run_getsockfd(const char* ip, int port){int sock = socket(AF_INET, SOCK_STREAM, 0);if( sock < 0){perror("socket()");exit(1);}/* 复用*/int opt = 1;setsockopt(sock , SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_addr.s_addr = inet_addr(ip);server.sin_port = htons(port);server.sin_family = AF_INET;if(bind(sock, (struct sockaddr *)&server, sizeof(server) ) < 0){perror("bind()");exit(2);}if(listen(sock, 5) < 0){perror("listen()");exit(3);}return sock;}/* 执行epoll逻辑 (创建,添加监听事件,等待返回)*/void run_epoll(int listen_sockfd){// 1. 创建 epoll 模型int epollfd = epoll_create(256);if( epollfd < 0){perror("epoll_create()");exit(4);}// 2. 添加监听描述符到模型中,并关心读事件struct epoll_event evt;evt.events = EPOLLIN;evt.data.ptr = new_fd_buf(listen_sockfd);epoll_ctl(epollfd, EPOLL_CTL_ADD,listen_sockfd, &evt);int nfds = 0;        /* 就绪事件个数 */int timeout = 1000;  /* 一秒超时  */while(1){nfds = epoll_wait(epollfd, evts, MAX_EVENTS, timeout );if( nfds < 0){perror("epoll_wait()");continue;}else if (nfds == 0){printf("epoll_wait() timeout\n");}else{/* 变量处理已经就绪的事件 */int idx_check = 0;for(; idx_check < nfds; idx_check++){fd_buf_p fp  = evts[idx_check].data.ptr;/* 监听socket 读事件就绪 */if( fp->fd == listen_sockfd &&\evts[idx_check].events & EPOLLIN ){/* 接受客户端的请求 */run_accept(epollfd, listen_sockfd);}/* 客户端socket 事件就绪 */else if ( fp->fd != listen_sockfd ){run_action(epollfd, idx_check);}}}}}static void* new_fd_buf(int fd){fd_buf_p ret  = (fd_buf_p)malloc(sizeof(fd_buf_t));assert( ret != NULL);ret->fd = fd;memset(ret->buf, 0, sizeof(ret->buf));return ret; }/* 处理客户端连接请求,并添加到epoll模型中 */void run_accept(int epollfd, int listen_sockfd){struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int new_sock = accept(listen_sockfd, (struct sockaddr*)&cliaddr, &clilen);if(new_sock < 0){perror("accept");return;}printf("与客户端连接成功: ip %s port %d \n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));/* 将与客户端连接的sockfd 添加到epoll模型中,并关心读事件 */struct epoll_event evt;evt.events = EPOLLIN;evt.data.ptr = new_fd_buf(new_sock); /* 为每一个客户端链接分配fd和缓冲区 */epoll_ctl(epollfd, EPOLL_CTL_ADD, new_sock ,&evt);}/* 处理客户端事件就绪 */void run_action(int epollfd, int index){/* 取得客户端结构体数据指针 */fd_buf_p fdp = evts[index].data.ptr;/* 读事件就绪 */if(evts[index].events & EPOLLIN){ssize_t s = read(fdp->fd, fdp->buf, sizeof(fdp->buf));if(s > 0){fdp->buf[s] = 0;printf("客户端请求消息:\n");printf("\n %s \n", fdp->buf);struct epoll_event evt; evt.events = EPOLLOUT;evt.data.ptr = fdp;epoll_ctl(epollfd, EPOLL_CTL_MOD, fdp->fd, &evt);}else if(s <= 0){/* 记得关闭socket,在epoll 模型中删除结点并释放分配的内存 */printf("\n客户端退出!\n");close(fdp->fd);epoll_ctl(epollfd, EPOLL_CTL_DEL, fdp->fd, NULL );free(fdp);}}else if(evts[index].events & EPOLLOUT){/* 写事件就绪 *//* 发送完毕后,记得做清理操作 */const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>Hello epoll!</h1></html>";write(fdp->fd, msg, strlen(msg));close(fdp->fd);epoll_ctl(epollfd, EPOLL_CTL_DEL, fdp->fd, NULL);free(fdp);}}


阅读全文
0 0
原创粉丝点击