说明:在子线程中启动epoll进行监听,收到报文且检查无误后,通过传入的函数指针转发报文。通过设定EPOLLOUT事件发送回复给客户端。
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/epoll.h>
#define BUF_SIZE 1024 //默认缓冲区
#define EPOLL_RUN_TIMEOUT -1 //epoll的超时时间
#define EPOLL_SIZE 10000 //epoll监听的客户端的最大数目
#define PHDR_LEN 5
#define MAX_PACKLEN 1024*10
//两个有用的宏定义:检查和赋值并且检测
#define CHK(eval) if(eval < 0){printf("error1\n");perror("error1");return NULL;}
#define CHK2(res, eval) if((res = eval) < 0){printf("error2\n");perror("error2");return NULL;}
static int listener=-1; //监听socket
static int epfd=-1; //epoll描述符
static int run=0;
typedef void (*pfun)(char *,int,int);
struct ipp
{
char *ip;
int port;
pfun fp;
};
struct evdata
{
int fd;
int len;
char *buf;
};
struct ipp ptr;
static pthread_t pid;
int epoll_sendresp(char *buf,int len,int fd);
static int check_packet(char *buf,int len);
static struct epoll_event events[EPOLL_SIZE];
int setnonblocking(int sockfd)
{
if(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK)<0)
return -1;
return 0;
}
void CloseAndDisable(int epfd, int sockid, struct epoll_event ee)
{
epoll_ctl(epfd, EPOLL_CTL_DEL, sockid, &ee);
shutdown(sockid,2);
close(sockid);
}
void *epoll_service_start(void *arg)
{
struct ipp *p=(struct ipp *)(arg);
struct sockaddr_in addr,their_addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(p->port);
addr.sin_addr.s_addr = inet_addr(p->ip);
socklen_t socklen;
socklen = sizeof(struct sockaddr_in);
static struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; //对读感兴趣,边沿触发
//char message[BUF_SIZE];
int client, res, epoll_events_count,sockfd;
CHK2(listener, socket(PF_INET, SOCK_STREAM, 0)); //初始化监听socket
setnonblocking(listener); //设置监听socket为不阻塞
int on=1;
setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
CHK(bind(listener, (struct sockaddr *)&addr, sizeof(addr))); //绑定监听socket
CHK(listen(listener, 10000)); //设置监听
CHK2(epfd,epoll_create(EPOLL_SIZE)); //创建一个epoll描述符,并将监听socket加入epoll
ev.data.fd = listener;
CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));
while(1)
{
CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));//由于是在子线程中启动epoll监听,故超时时间不可设为0,否则会导致CPU使用率居高不下。
int i;
for(i = 0; i < epoll_events_count ; i++)
{
if(events[i].data.fd == listener) //新的连接到来,将连接添加到epoll中,并发送欢迎消息
{
CHK2(client,accept(listener, (struct sockaddr *) &their_addr, &socklen));
setnonblocking(client);
ev.data.fd = client;
ev.events=EPOLLIN | EPOLLET;
CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev));//将客户端的fd加入epoll监听队列
}
else if(events[i].events&EPOLLIN)
{
if ((sockfd = events[i].data.fd) < 0)
continue;
char *message=NULL;
char hdr[6]={0};
int recvNum = 0;
int recved = 0;
int packlen=0;
int recv_lef = 0;
bool bReadOk = false;
recvNum = recv(sockfd, hdr, PHDR_LEN, 0);
packlen = *(int *)(hdr+1);
if (PHDR_LEN < packlen &&packlen > MAX_PACKLEN)
{
CloseAndDisable(epfd,sockfd, events[i]);
continue;
}
if(recvNum != PHDR_LEN && 0xff != hdr[0])
{
CloseAndDisable(epfd,sockfd, events[i]);
continue;
}
else
{
message = (char *)malloc(packlen);
memset(message, 0, packlen);
memcpy(message, hdr, PHDR_LEN);
recved = recvNum;
recv_lef = packlen-recved;
}
while(1)
{
// 确保sockfd是nonblocking的
recvNum = recv(sockfd, message+recved, recv_lef, 0);
if(recvNum < 0)
{
if(errno == EAGAIN)// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读 在这里就当作是该次事件已处理处.
{
bReadOk = true;
break;
}
else if (errno == ECONNRESET)// 对方发送了RST
{
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
else if (errno == EINTR)// 被信号中断
continue;
else
{
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
}
else if( recvNum == 0)// 这里表示对端的socket已正常关闭.发送过FIN了。
{
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
// recvNum > 0
recved += recvNum;
recv_lef -= recvNum;
if(recv_lef == 0)
{
bReadOk = true;
break; // 退出while(1),表示已经全部读完数据
}
else if(recv_lef < 0)
{
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
}
if(bReadOk==true)
{
if(check_packet(message,recved)<0)
{
printf("error packet\n");
CloseAndDisable(epfd,sockfd, events[i]);
}
else
p->fp(message,recved,sockfd);
if(message!=NULL)
{
free(message);
message = NULL;
}
/*
char *str="send resp to client";
epoll_sendresp(str,strlen(str),sockfd);
*/
}
}
else if(events[i].events&EPOLLOUT)//有数据待发送,写socket
{
struct evdata *data=(struct evdata *)events[i].data.ptr;
sockfd = data->fd;
bool bWritten = false;
int writenLen = 0;
int sent = 0;
int send_lef = data->len;
while(1)
{
// 确保sockfd是非阻塞的
writenLen = send(sockfd, data->buf+sent, send_lef, 0);
if (writenLen == -1)
{
if (errno == EAGAIN)
{
// 对于nonblocking 的socket而言,这里说明了已经全部发送成功了
bWritten = true;
break;
}
else if(errno == ECONNRESET)
{
// 对端重置,对方发送了RST
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
else if (errno == EINTR)// 被信号中断
continue;
}
if (writenLen == 0)
{
// 这里表示对端的socket已正常关闭.
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
// 以下的情况是writenLen > 0
sent += writenLen;
send_lef -= writenLen;
if(send_lef==0)
{
bWritten = true;
break;
}
else if(send_lef<0)
{
CloseAndDisable(epfd,sockfd, events[i]);
break;
}
break; // 退出while(1)
}
if (bWritten == true)
{
//printf("send suceess,send %d Byte\n",sent);
//设置用于读操作的文件描述符
ev.data.fd=sockfd;
//设置用于注测的读操作事件
ev.events=EPOLLIN | EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时接收数据
}
else
printf("send error,do not set EPOLLIN\n");
free(data->buf);
free(data);
}
}
}
shutdown(listener,2);
close(epfd);
listener=epfd=-1;
return ;
}
int epoll_stop()
{
pthread_cancel(pid);
if(listener>0)
shutdown(listener,2);
if(epfd>0)
close(epfd);
listener=epfd = -1;
run = 0;
return 0;
}
int epoll_start(char *ip,int port,pfun fp)
{
if(run>0)
{
printf("epoll has been running\n");
return -1;
}
memset(&ptr,0,sizeof(struct ipp));
ptr.ip = ip;
ptr.port = port;
ptr.fp = fp;
int rc = pthread_create(&pid, NULL, epoll_service_start, (void *)(&ptr));
if(rc<0)
{
printf("create thread fail\n");
return -1;
}
else
run = 1;
return 1;
}
int epoll_sendresp(char *msg,int len,int fd)
{
struct epoll_event ev;
if(epfd<0)
{
printf("epoll had stopped\n");
return -1;
}
struct evdata *data = (struct evdata *)malloc(sizeof(struct evdata));
memset(data,0,sizeof(struct evdata));
data->fd = fd;
data->len = len;
data->buf = (char *)malloc(len+1);
memset(data->buf,0,len+1);
memcpy(data->buf,msg,len);
ev.data.ptr = (void *)data;//下一轮epoll监听会监测到这个EPOLLOUT事件,对应的epoll_event.data.ptr会指向data;
//设置用于注测的写操作事件
ev.events = EPOLLOUT | EPOLLET;
//修改sockfd上要处理的事件为EPOLLOUT。
epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
return 1;
}
static int check_packet(char *msg,int len)
{
int packlen = *(int *)(msg+1);
printf("buf len is %d,pg_len is %d,buf is %s\n",len,packlen,msg);
if(len != packlen)
return -1;
if(msg[len-1] != 0x00)
printf("error inf\n");
return 1;
}