//现在用linux做服务器,高并发的epoll占了很重要部分吧。
//设计模式+epoll+fcntl全都可以开发了,这些如果没在项目中实践,要理解也是挺难的。
1 //《linux高性能服务器编程》9.3章
2 //略微改了些代码
3
4 #include <iostream>
5 using namespace std;
6
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <sys/epoll.h>
14 #include <fcntl.h>
15
16 #define MAX_EVENTS 1024
17 #define BUFFER_SIZE 10
18
19 //设置非阻塞
20 void setnonblocking(int fd)
21 {
22 int oldfd = fcntl(fd, F_GETFL);
23 int newfd = oldfd | O_NONBLOCK;
24 fcntl(fd, F_SETFL, newfd);
25 }
26
27 //epoll注册
28 void addfd(int epollfd, int fd, bool bET)
29 {
30 epoll_event event;
31 event.data.fd = fd;
32 event.events = EPOLLIN;
33
34 //设置ET模式
35 if(bET)
36 {
37 event.events |= EPOLLET;
38 }
39 epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
40 setnonblocking(fd);
41 }
42
43 void epollwork(epoll_event* events, int number, int epollfd, int listenfd, bool bET)
44 {
45 char buf[BUFFER_SIZE];
46 for(int i=0; i<number; i++)
47 {
48 int sockfd = events[i].data.fd;
49 //conn
50 if(sockfd == listenfd)
51 {
52 sockaddr_in cliAddr;
53 socklen_t cliLen = sizeof(cliAddr);
54 int connfd = accept(listenfd, (sockaddr*)&cliAddr, &cliLen);
55 if(connfd < 0 )
56 {cout<<"accept error"<<endl;continue;}
57
58 addfd(epollfd, connfd, true);
59
60 cout<<"conn:"<<connfd<<","<<endl;
61 }
62 //recv
63 else if(events[i].events & EPOLLIN)
64 {
65 //et
66 if(bET)
67 {
68 //ET循环处理内核缓存数据,
69 while(true)
70 {
71 memset(buf, 0, BUFFER_SIZE);
72 int ret = recv(sockfd, buf, sizeof(buf)-1, 0);
73 if(ret < 0)
74 {
75 if((errno == EAGAIN) || (errno == EWOULDBLOCK))
76 {break;}
77
78 close(sockfd);
79 break;
80 }
81 else if(ret == 0)
82 {
83 close(sockfd);
84 }
85 buf[ret] = '\0';
86 cout<<"recv:"<<buf<<endl;
87 }
88
89 }
90 //lt
91 else
92 {
93 //接收一次数据。如系统内核有缓存数据,epoll_wait能再次处理
94 memset(buf, 0, BUFFER_SIZE);
95 int ret = recv(sockfd, buf, sizeof(buf)-1, 0);
96 if(ret < 0)
97 {
98 close(sockfd);
99 continue;
100 }
101 buf[ret] = '\0';
102 cout<<"recv:"<<buf<<endl;
103 }
104 }
105 else
106 {
107 //todo send
108 }
109
110 }
111 }
112
113 int main(int argc, char** argv)
114 {
115 //默认为LT模式,启动附加参数就启用ET模式。便于调试
116 bool bET = false;
117 if(argc > 1)
118 {
119 bET = true;
120 }
121 const char* ip = "127.0.0.1";
122 int port = 9999;
123
124 //1.socket
125 int listenfd = socket(PF_INET, SOCK_STREAM, 0);
126 if(listenfd < 0)
127 {
128 cout<<"socket error"<<endl;
129 }
130
131 //2.bind
132 sockaddr_in addr;
133 addr.sin_family = AF_INET;
134 addr.sin_port = htons(port);
135 addr.sin_addr.s_addr = htonl(INADDR_ANY);
136 if(bind(listenfd, (sockaddr*)&addr, sizeof(addr)) == -1)
137 {
138 cout<<"bind error"<<endl;
139 }
140
141 //3.listen
142 if(listen(listenfd, 5) == -1)
143 {cout<<"listen error"<<endl;}
144
145 //4.epoll
146 epoll_event events[MAX_EVENTS];
147 //创建
148 int epollfd = epoll_create(50);
149 if(epollfd == -1)
150 {cout<<"epoll_create error"<<endl;}
151 //注册
152 addfd(epollfd, listenfd, bET);
153
154 //5.
155 while(true)
156 {
157 //获取
158 int ret = epoll_wait(epollfd, events, MAX_EVENTS, -1);
159 if(ret < 0)
160 {cout<<"epoll_wait error"<<endl;}
161
162 //处理
163 epollwork(events, ret, epollfd, listenfd, bET);
164 }
165
166 close(listenfd);
167 return 0;
168 }
169
170 //编译g++ -o epoll2 epoll2.cpp
171 //LT模式
172 //1.启动该服务,输入./epoll2
173 //2.ctrl+alt+f2,命令输入telnet 127.0.0.1 9999
174 //3.发送字符:aaaaabbbbbccccc 接收为:aaaaabbbb
175 // 发送字符: wjt 接收为:bcccccwjt
176
177 //ET模式
178 //1.启动该服务,输入./epoll2 et
179 //2.ctrl+alt+f2,命令输入telnet 127.0.0.1 9999
180 //3.发送字符:aaaaabbbbbccccc 接收为:aaaaabbbb
181 // 接收为:bccccc