| EPOLL 事件有两种模型:
									 Edge Triggered  (ET) 边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据。int op = EPOLLIN | EPOLLET;
									 //边缘触发,读接收缓冲区中的数据的时候,读完一部分数据就会减少一部分,减少的时候不会触发,只有当客户端再次发送数据,接收数据缓存区数据有一个上升状态才会触发epoll。         
									 不管你发多少数据,我一次只能读多少就读多少,读不完的也不会再次触发epoll_wait函数,下次再发数据在触发,就会连同上次没读完,仍然在缓存区的数据也接着读到
									 Level Triggered (LT) 水平触发 只要有数据都会触发。(默认就是这种)
									 当缓冲区有数据是,epoll_wait会不断得到触发(效率不高)如果一次发送的数据太多,超过接受数据数组的大小,当接受数据的数组读满后,read会再次触发epoll_wait函数,来接着读输入到缓冲区的数据
									 | 
| 新的场景
									 1、针对客户端的每一次发送的数据,epoll_wait只触发一次
										 2、我们想一次把缓冲区里边的数据全部读完(为了提高效率)     //要实现这两个步骤,就要把我们的描述符改为非阻塞状态,用while(1)一直读,直到返回-1,表示IO缓冲区里面的数据已经全部读走。
										 //阻塞状态一次读入数据送到我们定义的数组里,如果数组太小,IO缓冲区里的数据大,那么一次只能读满数组大小,后面的数据一直存在IO缓冲区里,等待下一个阻塞再读取。
										 | 
| //这里的情况就是设置了服务器端接收数据的数组char buf[10] , 客户端把数据发送到服务端接收数据缓存区,因为一次只能读取10的字节,所以当数据大于10的时候,一次没读完会接着触发epoll,接着读,直到读完。 | 
 在非阻塞情况下,读取对应的描述符,如果缓冲区为空,返回值为-1,errno为 EAGAIN
		
		| func.h | |
| #include <sys/types.h>
							 #include <sys/socket.h>
							 #include <netinet/in.h>
							 #include <arpa/inet.h>
							 #include <unistd.h>
							 | #include <string.h>
							 #include <stdio.h>
							 #include <stdlib.h>
							 #include <sys/epoll.h>
							 #include <errno.h>
							 #include <fcntl.h>
							 | 
| epoll_tcp_server.c | epoll_tcp_client.c | 
| #include "func.h"
							 //通过epoll来实现tcp即时通信
							 #define NUM 10
							 void change_noblock(int fd)        //设置文件描述符属性非阻塞
							 {
							         int status;
							         status=fcntl(fd,F_GETFL);
							         status=status|O_NONBLOCK;
							         int ret=fcntl(fd,F_SETFL,status);
							         if(-1==ret)
							         {
							                 perror("fcntl");
							                 return;
							         }
							 }
							 int main(int argc,char* argv[])
							 {
							         if(argc!=3)
							         {
							                 printf("error args\n");
							                 return -1;
							         }
							         int sfd=socket(AF_INET,SOCK_STREAM,0);
							         if(-1==sfd)
							         {
							                 perror("socket");
							                 return -1;
							         }
							         struct sockaddr_in ser;
							         memset(&ser,0,sizeof(ser));
							         ser.sin_family=AF_INET;
							         ser.sin_port=htons(atoi(argv[2]));    //一定要用htons                         
							         ser.sin_addr.s_addr=inet_addr(argv[1]);
							         int ret;
							         //给sfd绑定IP地址和端口号
							         ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
							         if(-1==ret)
							         {
							                 perror("bind");
							                 return -1;
							         }
							         ret=listen(sfd,NUM);
							         if(-1==ret)
							         {
							                 perror("listen");
							                 return -1;
							         }
							         int epfd=epoll_create(1);
							         struct epoll_event event,evs[NUM+2];
							         event.events=EPOLLIN;     //注册sfd
							         event.data.fd=sfd;
							         ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
							         if(-1==ret)
							         {
							                 perror("epoll_ctl");
							                 return -1;
							         }
							         event.events=EPOLLIN;     //注册标准输入
							         event.data.fd=0;
							         ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
							         if(-1==ret)
							         {
							                 perror("epoll_ctl");
							                 return -1;
							         }
							         int i;
							         int new_fd;
							         char buf[5];     //buf改小,为了看到边沿触发效果
							         int n;
							         while(1)
							         {
							                 memset(evs,0,sizeof(evs));
							                 ret=epoll_wait(epfd,evs,NUM+2,-1);
							                 if(ret >0)
							                 {
							                         for(i=0;i<ret;i++)
							                         {
							                                 if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)                
							                                 {
							                                         new_fd=accept(sfd,NULL,NULL);
							                                         printf("new_fd =%d\n",new_fd);
							                                         event.events=EPOLLIN | EPOLLET;  //边沿触发的是否可读
							                                         change_noblock(new_fd);  //改变描述符的属性为非阻塞,这个放前放后都可以,只要修改,没关闭之前在哪都是修改完的属性
							                                         event.data.fd=new_fd;
							                                         epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);        
							                                 }
							                                 if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
							                                 {
							                                         memset(buf,0,sizeof(buf));
							                                         n=read(0,buf,sizeof(buf));
							                                         if(n>0)
							                                         {
							                                                 send(new_fd,buf,strlen(buf)-1,0);
							                                         }else if(n==0)
							                                         {
							                                                 printf("bye\n");
							                                                 event.events=EPOLLIN;
							                                                 event.data.fd=new_fd;
							                                                 epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
							                                                 close(new_fd);        
							                                         }
							                                 }
							                                 if(evs[i].events == EPOLLIN &&evs[i].data.fd == new_fd)
							                                 {
							                                         while(1)
							 //while(1) 一直读IO缓冲区里面的数据,直到全部读走,返回EAGAIN;由于newfd描述符属性已经改为非阻塞,监听事件改为边缘触发,IO缓冲区里数据没有读完不会再次触发描述符,所以这里while(1)要IO缓冲区数据一次读完;  对端发一次,我们这边只触发一次,不会一直触发;
							 //这个例子是单线程,多线程的情况下可以交给其他线程处理
							                                         {
							                                                 memset(buf,0,sizeof(buf));
							                                                 n=recv(new_fd,buf,sizeof(buf),0);
							                                                 if(n>0)
							                                                 {
							                                                         printf("%s",buf);
							                                                 }else if(n == -1 && errno == EAGAIN)
							 //加上errno判断是因为recv函数失败也是返回-1;recv只有描述符是非阻塞的才会返回-1,默认阻塞情况下只有函数出错才返回-1;
							                                                 {
							                                                         break;
							                                                 }else if(n==0){
							                                                         printf("Bye\n");
							                                                         event.events=EPOLLIN;
							                                                         event.data.fd=new_fd;
							                                                         epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
							                                                         close(new_fd);
							                                                 }
							                                         }
							                                         printf("\n");   //刷新缓冲区,输出数据
							 //前面printf都是是把数据送到输出缓冲区,必须回车或者fflush(stdout)才能把接收到的数据送到输出终端;使用fflush(stdout);后还是要加一个输出一个回车
							 //这个回车符可以不要,但是要客户端发送数据的时候不要strlen(len)-1,要连同换行符一起发过来;
							                                 }        
							                         }
							                 }
							         }
							         return 0;
							 }
							 | #include "func.h"
							 void change_noblock(int fd)        //设置文件描述符属性非阻塞
								 {
								         int status;
								         status=fcntl(fd,F_GETFL);
								         status=status|O_NONBLOCK;
								         int ret=fcntl(fd,F_SETFL,status);
								         if(-1==ret)
								         {
								                 perror("fcntl");
								                 return;
								         }
								 }
								 int main(int argc,char** argv)
							 {
							         if(argc !=3)
							         {
							                 printf("error args\n");
							                 return -1;
							         }
							         int sfd=socket(AF_INET,SOCK_STREAM,0);
							         if(-1==sfd)
							         {
							                 perror("socket");
							                 return -1;
							         }
							         struct sockaddr_in ser;
							         memset(&ser,0,sizeof(ser));
							         ser.sin_family=AF_INET;
							         ser.sin_port=htons(atoi(argv[2]));                                            //一定要用htons
							         ser.sin_addr.s_addr=inet_addr(argv[1]);
							         int ret;
							         ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
							         if(-1==ret)
							         {
							                 perror("connect");
							                 return -1;
							         }
							         int epfd=epoll_create(1);
							         struct epoll_event event,evs[2];
							         event.events = EPOLLIN | EPOLLET; 
							         event.data.fd=sfd;
							         change_nonblock(sfd);
							         ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
							         if(-1==ret)
							         {
							                 perror("epoll_ctl");
							                 return -1;
							         }
							         event.events=EPOLLIN;
							         event.data.fd=0;
							         ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
							         if(-1==ret)
							         {
							                 perror("epoll_ctl");
							                 return -1;
							         }
							         int i;
							         char buf[5];
							         int n;
							         while(1)
							         {
							                 memset(evs,0,sizeof(evs));
							                 ret=epoll_wait(epfd,evs,2,-1);
							                 if(ret>0)
							                 {
							                         for(i=0;i<ret;i++)
							                         {
							                                 if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
							                                 {
							                                         memset(buf,0,sizeof(buf));
							                                         n=read(0,buf,sizeof(buf));                    
							                                         if(n==0)
							                                         {
							                                                 printf("bye\n");
							                                                 close(sfd);
							                                                 return 0;
							                                         }
							                                         n=send(sfd,buf,strlen(buf)-1,0);
							 //这里如果不减1,对端最后就不用printf("\n");刷新缓冲区,输出接收到的数据
							                                         if(-1==n)
							                                         {
							                                                 perror("send");
							                                                 return -1;
							                                         }
							                                 }
							                                 if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
							                                 {
							                                         while(1)
							                                         {
							                                             memset(buf,0,sizeof(buf));
							                                             n=recv(sfd,buf,sizeof(buf),0);            
							                                             if(n > 0)
							                                             {
							                                                 printf("%s\n",buf);
							                                             }
							                                             else if(-1==n&&errno==EAGAIN)
								                                             {   
								                                                 //close(sfd);
								                                                 break;
								                                              }
								                                              else if(n==0)
							                                              {
							                                                 printf("bye\n");
							                                                 close(sfd);
							                                                 return 0;
							                                              }
							                                          }
							                                 }
							                         }
							                 }
							         }
							         return 0;
							 }
							 | 
 
                     
                    
                 
                    
                 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号