(五)epoll实现TCP服务端

来源:互联网 发布:剑三明星捏脸数据 编辑:程序博客网 时间:2024/06/11 22:08

1.epoll是由poll改进而来的。

2.epoll跟poll在编程实现上有一定程度相似。

3.epoll主要通过结构体 epoll_event 来实现监听。

(!!!!!!不想看原理的可以直接拉到后面看代码!!!!!)


//epoll_event结构体struct epoll_event  {    uint32_t events;                         /* Epoll events */    epoll_data_t data;                     /* User data variable */  } __attribute__ ((__packed__));    typedef union epoll_data  {    void *ptr;    int fd;    uint32_t u32;    uint64_t u64;  } epoll_data_t;  

虽然我知道贴出来可能都没卵用,我还是直接说吧。epoll编程的几个步骤:
1.创建 epoll_event 类型的结构体2个, 如: struct epoll_event events,wait_events 前者是用于把套接字描述符加到 epoll句柄中的,后者是用于判断发生了什么事件
struct epoll_event event;             //用于往epoll句柄中加入新的需要监测的套接字
struct epoll_event wait_event;        //用于装epoll_wait()的返回信息,判断哪一个套接字发生响应


2.用 epoll_create()创造一个 epoll句柄: 
int epfd = epoll_create(10);      //这个10没什么大概意义,只要是正整数即可。


3.给结构体epoll_event 赋值并加入到 epoll句柄 epfd中。epoll_ctl()
event.data.fd = sockfd;      //sockfd为 socket()函数返回的监听套接字
event.events = EPOLLIN;     //设置期望该套接字发生的事件为可读事件
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);  //EPOLL_CTL_ADD表示往epfd中加入新的套接字来检测


4.等待套接字响应。epoll_wait()
int ret = epoll_wait(epfd,&wait_event,num,-1);
//解释一下上面的这个函数:
第一个参数:epoll句柄(由epoll_create()返回的)
第二个参数:struct epoll_event 类型的结构体,放在该位置,当epoll_wait返回时,就会把响应的套接字信息放到这个结构体中。
第三个参数:正在监测的套接字个数。
第四个参数:-1表示一直阻塞直到有套接字发生响应,0表示不阻塞,无论有没有套接字响应 都返回,>0表示阻塞的时间,毫秒为单位。
这个函数的返回值ret 为发生响应的套接字的个数。


5.判断epfd中哪个套接字发生响应
if((wait_event.data.fd == sockfd)&&(wait_event.events & EPOLLIN ==EPOLLIN))   //若套接字sockfd发生响应,并且为可读
{......}

下面给出代码:
服务端:

#include "myhead.h"#define MAX 50void write_to_all(char *wbuf);    //此函数用于把一个客户端发来的消息转发给其他客户端int client_fd[50];       //此数组用于装连接进来的客户端的套接字文件描述符int main(){int sockfd,newsockfd;struct sockaddr_in saddr;int size = sizeof(struct sockaddr_in);//创建2个epoll_event结构体struct epoll_event event;//用于往epoll句柄加入新的待检测套接字struct epoll_event wait_event;   //用于装epoll_wait()返回的响应的套接字的消息,判断哪个套接字发生响应int on = 1;int ret;//设置TCP套接字的一些属性bzero(&saddr,size);saddr.sin_family = AF_INET;saddr.sin_port = htons(8888);saddr.sin_addr.s_addr = htonl(INADDR_ANY);sockfd = socket(AF_INET,SOCK_STREAM,0);setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//设置端口复用,不设也可以,对功能没影响bind(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));listen(sockfd,10);//epoll 相关参数int max = 0;memset(client_fd,-1,sizeof(client_fd));int epfd;//创建epoll句柄epfdepfd = epoll_create(10);   //这是10没啥意义,只要是正数即可。//加入监听套接字event.data.fd = sockfd;event.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);//加入标准输入,使服务端可以发信息event.data.fd = 0;event.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);int i;while(1){//等待套接字发生某种响应,否则一直阻塞ret = epoll_wait(epfd,&wait_event,max+2,-1);//判断是否有新客户端连入if((wait_event.data.fd ==sockfd) && (wait_event.events &EPOLLIN == EPOLLIN)){int len = sizeof(struct sockaddr);struct sockaddr_in caddr;newsockfd = accept(sockfd,(struct sockaddr*)&caddr,&len);//newsockfd加入client_fdfor(i=0;i<MAX;i++){if(client_fd[i] != -1)continue;else{client_fd[i] = newsockfd;break;}}//newsockfd加入epoll句柄 epfdevent.data.fd = newsockfd;event.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,newsockfd,&event);max+=1;ret --;//处理完一个响应后,ret减一if(ret<=0)//若ret已经为0,则说明已经处理完所有的发生响应的套接字,就可以继续epoll_wait()等待套接字发生再响应{continue;}}if((wait_event.data.fd ==0)&&(wait_event.events & EPOLLIN==EPOLLIN)){char wbuf[50]={0};bzero(wbuf,50);scanf("%s",wbuf);write_to_all(wbuf);ret-=1;}//判断有没有其他客户端的数据发来for(i=0;i<MAX;i++){char rbuf[50]={0};if((wait_event.data.fd ==client_fd[i])&&(wait_event.events&EPOLLIN==EPOLLIN)){int recv_num;bzero(rbuf,50);recv_num = recv(client_fd[i],rbuf,50,0);if(recv_num == 0){client_fd[i] = -1;close(client_fd[i]);max -=1;}else{printf("%s\n",rbuf);bzero(rbuf,50);}ret-=1;if(ret<=0)break;}}}}void write_to_all(char *wbuf){int i;for(i=0;i<MAX;i++){if(client_fd[i] !=-1){write(client_fd[i],wbuf,50);}}}

客户端:
客户端有很多种写法,这里就给出一种用poll写的,因为也懒得再写,就用之前用开的客户端代码:

#include"myhead.h"char rbuf[50];char wbuf[50];char ipbuf[50];int main(){int sockfd;int ret,port;struct pollfd pollfd[2];struct sockaddr_in saddr;int size = sizeof(struct sockaddr_in);bzero(&saddr,size);saddr.sin_family = AF_INET;saddr.sin_port = htons(8888);saddr.sin_addr.s_addr = inet_addr("192.168.152.128");sockfd = socket(AF_INET,SOCK_STREAM,0);ret=connect(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));if(ret == 0){inet_ntop(AF_INET,(void*)&saddr.sin_addr.s_addr,ipbuf,50);port = ntohs(saddr.sin_port);printf("%s,%d\n",ipbuf,port);}pollfd[0].fd = STDIN_FILENO;pollfd[0].events = POLLIN;pollfd[1].fd = sockfd;pollfd[1].events = POLLIN;while(1){poll(pollfd,2,-1);if((pollfd[0].revents & POLLIN)==POLLIN){puts("message from keyboard");bzero(wbuf,50);scanf("%s",wbuf);write(sockfd,wbuf,50);}if((pollfd[1].revents & POLLIN)==POLLIN){bzero(rbuf,50);read(sockfd,rbuf,50);printf("%s\n",rbuf);}}}



0 0
原创粉丝点击