参考 <https://blog.youkuaiyun.com/daaikuaichuan/article/details/83862311>
参考 <https://www.bilibili.com/video/BV1iJ411S7UA>
求职期间,还是得好好学习。
看了大概的思路,自己理解着敲一下。和原来代码的有些地方不同。
视频有提到,为什么反应堆模式要注册写事件,因为可能写缓存满了(对方接收窗口小了,接收不过来)。
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#define MAX_EVENTS 1024 /*监听上限*/
#define BUFLEN 4096 /*缓存区大小*/
#define SERV_PORT 6666 /*端口号*/
/*描述就绪文件描述符的相关信息*/
struct myevent_s{
int fd; //要监听的文件描述符
int events; //对应的监听事件,EPOLLIN和EPLLOUT
void *arg; //指向自己结构体指针
void (*callbak)(int fd,int events,void* arg);//回调函数
int status; //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
char buf[BUFLEN];
int len;
long last_active; //记录每次加入红黑树 g_efd 的时间值
};
int g_efd; //全局变量,作为红黑树根
struct myevent_s g_events[MAX_EVENTS+1]; //自定义结构体类型数组. +1-->listen fd
void eventinit(struct myevent_s *ev,int fd,void *arg){
ev->fd = fd;
ev->events = 0;
ev->arg = arg;
ev->callbak = NULL;
ev->status = 0;
memset(ev->buf,0,BUFLEN);
ev->len = 0;
ev->last_active = time(NULL);
}
void eventadd(int efd,struct myevent_s *ev,int events,void (*callbak)(int fd,int events,void* arg)){
if(ev->status == 1){
return;
}
struct epoll_event epv;
int op;
ev->events = events;
ev->callbak = callbak;
ev->last_active = time(NULL);
epv.data.ptr = (void*)ev;
epv.events = events;
op = EPOLL_CTL_ADD;
ev->status = 1;
epoll_ctl(efd,op,ev->fd,&epv);
return;
}
void eventdel(int efd,struct myevent_s *ev){
ev->status = 0;
epoll_ctl(efd,EPOLL_CTL_DEL,ev->fd,NULL);
}
void senddata(int fd, int events, void *arg);
void recvdata(int fd, int events, void *arg);
void recvdata(int fd, int events, void *arg){
struct myevent_s *ev = (struct myevent_s *)arg;
ev->len = recv(fd,ev->buf,sizeof(ev->buf),0);
printf("fd[%d] recv [%d]%s\n",fd,ev->len,ev->buf);
eventdel(g_efd,ev);
if(ev->len > 0){
eventadd(g_efd,ev,EPOLLOUT|EPOLLET,senddata);
}
else if(ev->len == 0){
close(fd);
ev->fd = -1;
}else{
if(errno == EINTR){
eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);
}else{
printf("%d %s\n",errno,strerror(errno));
close(fd);
ev->fd = -1;
}
}
return;
}
void senddata(int fd, int events, void *arg){
struct myevent_s *ev = (struct myevent_s *)arg;
int len = send(fd,ev->buf,ev->len,0);
printf("fd[%d] send [%d]%s\n",fd,len,ev->buf);
eventdel(g_efd,ev);
if(len > 0){
eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);
}
else if(len == 0){
close(fd);
ev->fd = -1;
}else{
if(errno == EINTR){
eventadd(g_efd,ev,EPOLLOUT|EPOLLET,senddata);
}else{
printf("%d %s\n",errno,strerror(errno));
close(fd);
ev->fd = -1;
}
}
return;
}
void acceptconn(int fd,int events,void *arg){
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
client_fd = accept(fd,(struct sockaddr*)&client_addr,&client_addr_len);
if(client_fd < 0){
printf("%d %s\n",errno,strerror(errno));
return;
}
char buf[20];
printf("accpet %s:%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr,buf,sizeof(buf)),
ntohs(client_addr.sin_port));
int i;
for(i=0;i<MAX_EVENTS;i++){
if(g_events[i].fd == -1){
break;
}
}
if(i == MAX_EVENTS){
printf("full\n");
close(client_fd);
return;
}
int flag = fcntl(client_fd,F_GETFL);
fcntl(client_fd,F_SETFL,flag|O_NONBLOCK);
struct myevent_s *ev = &g_events[i];
eventinit(ev,client_fd,(void*)ev);
eventadd(g_efd,ev,EPOLLIN|EPOLLET,recvdata);
return;
}
void initlistensocket(int efd,short port){
struct sockaddr_in addr;
int listen_fd;
listen_fd = socket(AF_INET,SOCK_STREAM,0);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listen_fd,(struct sockaddr*)&addr,sizeof(addr));
listen(listen_fd,5);
struct myevent_s *ev = &g_events[MAX_EVENTS];
eventinit(ev,listen_fd,(void*)ev);
eventadd(g_efd,ev,EPOLLIN,acceptconn);
return;
}
int main(){
int i;
for(i=0;i<MAX_EVENTS+1;i++){
g_events[i].fd = -1;
}
g_efd = epoll_create(MAX_EVENTS+1);
initlistensocket(g_efd,SERV_PORT);
struct epoll_event events[MAX_EVENTS + 1];
int nfd;
struct myevent_s *ev;
printf("server running:port[%d]\n", SERV_PORT);
int checkpos = 0;
long now,duration;
for(;;){
nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000);
if(nfd < 0){
printf("%d %s\n",errno,strerror(errno));
if(errno == EINTR){
continue;
}else{
break;
}
}
if(nfd == 0){//超时
now = time(NULL);
for(i=0;i<100;i++,checkpos++){
if(checkpos == MAX_EVENTS);
checkpos = 0;
if(g_events[checkpos].fd ==-1 || g_events[checkpos].status != 1)
continue;
duration = now -g_events[checkpos].last_active;
if(duration >= 60)
{
printf("[fd=%d] timeout\n", g_events[checkpos].fd);
eventdel(g_efd, &g_events[checkpos]);
close(g_events[checkpos].fd);
g_events[checkpos].fd = -1;
}
}
}
for(i=0;i<nfd;i++){
ev = (struct myevent_s *)events[i].data.ptr;
//如果监听的是读事件,并返回的是读事件
if((events[i].events & EPOLLIN) &&(ev->events & EPOLLIN))
{
ev->callbak(ev->fd, events[i].events, ev->arg);
}
//如果监听的是写事件,并返回的是写事件
else if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT))
{
ev->callbak(ev->fd, events[i].events, ev->arg);
}
}
}
}