epoll:结合了select与poll的优点,以及优化了它们的不足,来实现同时控制多个句柄,以此来实现多路复用。它也是使用文件系统的相关信息来实现的
它所使用的三个系统调用函数
1.epoll_create函数
创建一个句柄,size大小可不关心,该句柄会占用一个文件描述符位置
2.epoll_ctl函数,它需要使用一个结构体告诉内核需监听什么事件
它为一个事件注册函数,先将要监听的何种事件进行注册,不同于select函数,它是在监听的时候就要告诉是何种事件
op指要对某个描述符进行何种操作(添加,删除,更改)
epoll_event该结构体第一个参数为要监听的事件类型,第二个参数为一个联合体类型,可为一个fd,也可为一个指针
3.使用epoll_wait函数
使用该函数它只需检查哪个就绪队列(链表)是否为空,用它可以获得已经就绪的描述符
maxevents表示一次最多允许返回多少个就绪事件个数
events是已经分配好的结构体数组(需要用户自己分配),不能为空
避免让客户端等待2MSL时间
当客户端要进行四次挥手时,发送FIN后,会进入一个TIME-WAIT状态等待2MSL,此时它与服务器端的连接还未真正断开,因此在该时间内该用户的端口号等信息不能被其它用户所使用。因此为了避免等待2MSL,要使用setsockopt函数来实现端口复用功能
epoll的两种工作方式:
LT:它支持阻塞套接字和非阻塞套接字,它读取缓冲区中的数据时可以读一部分,因为当一个事件对应的套接字的缓冲区中还有内容时,内核会每次都告知你某个描述符已经就绪,可以进行IO操作
ET(相对高效):数据只有从网络上到达时才会通知你一次,如果没有任何的状态改变,若一次没有将某事件对应的套接字缓冲区内容处理完,它会一直在读或写时组等等待,这样会使后续的多个描述符一直等待。因此必须将套接字设置为非阻塞的,这样当读或写返回一个EAGAIN时才需要等待,即当读到的数据小于请求数据长度时说明缓冲区中的数据已经读完。
服务器端:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <errno.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <assert.h>
10 #include <unistd.h>
11 #include <sys/epoll.h>
12 #include<fcntl.h>
13 #define _MAX_ 64
14 #define _MAX_SIZE_ 1024
15 #define BACK_LOG 5
16 typedef struct epoll_buf
17 {
18 int fd;
19 char buf[_MAX_SIZE_];
20 }epoll_t,*epoll_data_p;
21 static void Usage(const char* proc)
22 {
23 assert(proc);
24 printf("Usage:%s [ip] [port]\n",proc);
25 }
26
27 static void SetNonBlock(int fd)
28 { //set fd to nonblock
29 int fds=0;
30 if(fds=fcntl(fd,F_GETFL)<0)
31 {
32 perror("fcntl");
33 exit(1);
34 }
35 if(fcntl(fd,F_SETFL,fds|O_NONBLOCK)<0)
36 {
37 perror("fcntl");
38 exit(2);
39 }
40
41 }
42 static int startup(char *ip,int port)
43 {
44 assert(ip);
45 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
46 if(listen_sock < 0)
47 {
48 perror("socket");
49 exit(1);
50 }
51 struct sockaddr_in local;
52 local.sin_family=AF_INET;
53 local.sin_port=htons(port);
54 local.sin_addr.s_addr=inet_addr(ip);
55 socklen_t len=sizeof(local);
56 int opt=1; //让客户端避免2MSL时间,实现端口复用
57 setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
58 if(bind(listen_sock,(struct sockaddr*)&local,len)<0)
59 {
60 perror("bind");
61 exit(2);
62 }
63 if(listen(listen_sock,BACK_LOG)<0)
64 {
65 perror("listen");
66 exit(3);
67 }
68 return listen_sock;
69 }
70 int read_data(int fd,char *buf,int size)
71 {
72 assert(buf);
73 int ret=0;
74 int index=0;
75 memset(buf,'\0',sizeof(buf));
76 while((ret=read(fd,buf+index,size-index))<size)
77 {
78 if(errno==EAGAIN) //客户端已无数据发送
79 {
80 break;
81 }
82 index+=ret;
83 }
84 return index;
85
86 }
87 int write_data(int fd,char* buf,int size)
88 {
89 assert(buf);
90 int ret=0;
91 int index=0;
92 while((ret=write(fd,buf+index,size-index))<size)
93 {
94 if(errno==EAGAIN)
95 break;
96 index+=ret;
97 }
98 return index;
99 }
100 static int epoll_server(int listen_sock)
101 {
102 int epoll_fd=epoll_create(256);
103 if(epoll_fd < 0)
104 {
105 perror("epoll_create");
106 exit(1);
107 }
108 int ready_num=-1;
109 struct epoll_event ev;
110 ev.events=EPOLLIN|EPOLLET;
111 ev.data.fd=listen_sock;
112 SetNonBlock(listen_sock);//将套接字设置为非阻塞,因使用ET工作模式
113 struct epoll_event ret_ev[_MAX_];
114 int num=_MAX_;
115 int timeout=5000;
116 struct sockaddr_in client;
117 socklen_t size=sizeof(client);
118 int i=0;
119 if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ev)<0)
120 {
121 perror("epoll_ctl");
122
123 exit(1);
124 }
125 int done=0;
126 while(!done)
127 {
128 switch(ready_num=epoll_wait(epoll_fd,ret_ev,num,timeout))
129 {//只需检查就绪队列(就绪链表)是否为空
130 case 0:
131 printf("timeout...\n");
132 break;
133 case -1:
134 perror("epoll_wait");
135 break;
136 default:
137 {
138 for(i=0;i<ready_num;++i)
139 {
140 if(ret_ev[i].data.fd==listen_sock &&\
141 (ret_ev[i].events &EPOLLIN))
142 {//判断是否处于监听状态且所关心的事件类型
143 int fd=ret_ev[i].data.fd;
144 int new_sock=accept(fd,(struct sockaddr*)&client ,&size);
145 if(new_sock < 0)
146 {
147 perror("accept");
148 exit(1);
149 }
150 printf("get a new connect...\n");
151 SetNonBlock(new_sock);
152 ev.data.fd=new_sock;
153 ev.events=EPOLLIN|EPOLLET;
154 if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev )<0)
155 {//将new_sock添加到epoll中
156 perror("epoll_ctl");
157 exit(2);
158 }
159 }else if(ret_ev[i].events&EPOLLIN)
160 {
161 int fd=ret_ev[i].data.fd;
162
163 epoll_data_p msg ;
164 msg=(epoll_data_p)malloc(sizeof(epoll_t));
165 if(!msg)
166 {
167 perror("malloc");
168 exit(1);
169 }
170 msg->fd=fd;
171 // ssize_t _s=read(msg->fd,msg->buf,\
172 sizeof((msg->buf))-1);
173 int _s=read_data(msg->fd,msg->buf,\
174 sizeof(msg->buf)-1);
175 if(_s > 0)
176 {
177 msg->buf[_s]='\0';
178 printf("client:%s",msg->buf);
179 ev.data.ptr=msg;
180 ev.events=EPOLLOUT|EPOLLET;
181 if(epoll_ctl(epoll_fd,EPOLL_CTL_MOD,msg->fd, &ev)<0)
182 {
183 perror("epoll_ctl");
184 return -1;
185 }
186 }else if(_s==0)
187 {
188 printf("client is closed...\n");
189 free(msg);
190 close(msg->fd);
191 epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
192
193 }
194 }else if(ret_ev[i].events& EPOLLOUT)
195 { //write ready
196 epoll_data_p msg=(epoll_data_p)ret_ev[i].data.p tr;
197 // write(msg->fd,msg->buf,strlen(msg->buf));
198 write_data(msg->fd,msg->buf,sizeof(msg->buf));
199 // close(msg->fd);
200 // char* mem="HTTP/1.0 200 OK\r\n\r\n hello worl d\r\n";
201
202 //write(msg->fd,mem,strlen(mem));
203 ev.data.fd=msg->fd;
204 ev.events=EPOLLIN|EPOLLET;
205 epoll_ctl(epoll_fd,EPOLL_CTL_MOD,msg->fd,&ev);
206 //close(msg->fd);
207 // epoll_ctl(epoll_fd,EPOLL_CTL_DEL,msg->fd,NULL );
208 //free(msg);
209 }
210
211 }
212
213 }
214 break;
215 }
216 }
217 return epoll_fd;
218
219 }
220 int main(int argc,char* argv[])
221 {
222 if(argc!=3)
223 {
224 Usage(argv[0]);
225 exit(1);
226 }
227 char *_ip=argv[1];
228 int _port=atoi(argv[2]);
229 int listen_sock=startup(_ip,_port);
230 epoll_server(listen_sock);
231 return 0;
232 }
客户端代码
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 #include <sys/types.h>
6 #include <errno.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <sys/socket.h>
10
11 static void Usage(const char* proc)
12 {
13 assert(proc);
14 printf("Usage:%s [ip] [port]\n",proc);
15 }
16
17 int main(int argc,char* argv[])
18 {
19 if(argc!=3)
20 {
21 Usage(argv[0]);
22 exit(1);
23 }
24 int sock=socket(AF_INET,SOCK_STREAM,0);
25 if(sock < 0)
26 {
27 perror("socket");
28 exit(2);
29 }
30 char *_ip=argv[1];
31 int _port=atoi(argv[2]);
32 struct sockaddr_in remote;
33 remote.sin_family=AF_INET;
34 remote.sin_port=htons(_port);
35 remote.sin_addr.s_addr=inet_addr(_ip);
36 socklen_t len=sizeof(remote);
37 if(connect(sock,(struct sockaddr*)&remote,len)<0)
38 {
39 perror("connect");
40 return -1;
41 }
42
43 char buf[1024];
44 while(1)
45 {
46 memset(buf,'\0',sizeof(buf));
47 printf("please input:");
48 fflush(stdout);
49 ssize_t _size=read(0,buf,sizeof(buf)-1);
50 if(_size >0)
51 {
52 buf[_size]='\0';
53 write(sock,buf,strlen(buf));
54 }
55
56 _size=read(sock,buf,sizeof(buf)-1);
57 if(_size>0)
58 {
59 printf("server---client:%s\n",buf);
60 }
61 }
62 close(sock);
63 return 0;
64 }
运行结果:
使用浏览器为客户端:
转载于:https://blog.51cto.com/10541559/1785973