socket编程之Epoll
使用Epoll相较select的好处没有文件描述符的限制(MAX_FILES),select默认的1024.工作效率不会随着描述符的增加而下降(没有改变的描述符不会返回,也就是不会遍历)经过内核级优化的Epoll的事件触发模式Level Trigger 水平触发,如果数据没有一次性处理完毕,会再次发送Edge Trigger 边沿触发,不管数据有没有一次性处理完毕,都不会再次发送(效率最高,难度大
·
使用Epoll相较select的好处
- 没有文件描述符的限制(MAX_FILES),select默认的1024.
- 工作效率不会随着描述符的增加而下降(没有改变的描述符不会返回,也就是不会遍历)
- 经过内核级优化的
Epoll的事件触发模式
Level Trigger 水平触发,如果数据没有一次性处理完毕,会再次发送
Edge Trigger 边沿触发,不管数据有没有一次性处理完毕,都不会再次发送(效率最高,难度大)
Epoll重要的API
int epoll_create(); //创建一个epoll实例
int epoll_ctl(epfd,op,fd,struct epoll_event *event);//向epoll添加事件
int epoll_wait(epfd,events,maxevents,timeout);//等待事件跟seclect差不多
Epoll重要的事件
EPOLLET 通过它可以设置边沿模式
EPOLLIN
EPOLLOUT
EPOLLPRI 出现中断
EPOLLERR 读写出现异常
EPOLLHUP 挂起
epoll_ctl 相关操作
EPOLL_CTL_ADD 将文件描述符add到epoll中去
EPOLL_CTL_MOD 对已经加入到epoll的文件描述符进行修改
EPOLL_CTL_DEL 将已经加入到epoll中的文件描述符删除掉
Epoll重要的结构体
typedef union epoll_data{//用union修饰,一次只能使用其中一项属性
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;//in out err等事件
epoll_data_t data;//可以放自己的数据,比如文件描述符
};
代码实例(服务端)
#include <iostream>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define PORT 8111
#define MESSAGE_LEN 1024
#define MAX_EVENTS 20
#define TIMEOUT 500
int main(int argc,char* argv[]){
int socketfd,epoll_fd,event_number,flags;
int on=1;
int ret=-1;
int backlog = 10;
int accept_fd = -1;
struct epoll_event ev,events[MAX_EVENTS];
struct sockaddr_in localaddr,remoteaddr;
char in_buff[MESSAGE_LEN]={0,};
socketfd = socket(AF_INET,SOCK_STREAM, 0);
if(socketfd == -1){
std::cout<<"failed to create socket"<<std::endl;
exit(-1);
}
flags = fcntl(socketfd,F_GETFL,0);
fcntl(socketfd,F_SETFL,flags| O_NONBLOCK);
ret = setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(ret == -1){
std::cout<<"failed to set socket options"<<std::endl;
}
localaddr.sin_family = AF_INET;
localaddr.sin_port = PORT;
localaddr.sin_addr.s_addr = INADDR_ANY;
bzero(&(localaddr.sin_zero),8);
ret = bind(socketfd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));
if(ret == -1){
std::cout<<"fail to bind"<<std::endl;
exit(-1);
}
ret = listen(socketfd,backlog);
if(ret == -1){
std::cout<<"fail to listen"<<std::endl;
exit(-1);
}
epoll_fd = epoll_create(256);
ev.events = EPOLLIN;
ev.data.fd = socketfd;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socketfd,&ev);
for(;;){
event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);
for(int i=0;i<event_number;i++){
if(events[i].data.fd == socketfd){
socklen_t addr_len = sizeof(struct sockaddr);
accept_fd = accept(socketfd,(struct sockaddr *) &remoteaddr,
&addr_len
);
flags = fcntl(accept_fd,F_GETFL,0);
fcntl(accept_fd,F_SETFL,flags| O_NONBLOCK);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = accept_fd;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);
}else if(events[i].events & EPOLLIN){
do{
memset(in_buff,0,MESSAGE_LEN);
ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
if(ret == 0){
close(events[i].data.fd);
}
if(ret == MESSAGE_LEN){
std::cout<< "maybe have more data"<<std::endl;
}}while(ret < 0 && errno == EINTR);
if(ret < 0){
switch(errno){
case EAGAIN:
std::cout<<"no hava more data..."<<std::endl;
break;
default:
break;
}
}
std::cout<<"rev:"<<in_buff<<std::endl;
send(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
}
}
}
close(socketfd);
return 0;
}
Epoll+fork优化
当然cpu还得充分利用才nice,因此可以引入fork优化
代码(服务端)
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define PORT 8111
#define MESSAGE_LEN 1024
#define MAX_EVENTS 20
#define TIMEOUT 500
#define MAX_PROCESS 4
int main(int argc,char* argv[]){
int socketfd,epoll_fd,event_number,flags;
pid_t pid=-1;
int on=1;
int ret=-1;
int backlog = 10;
int accept_fd = -1;
struct epoll_event ev,events[MAX_EVENTS];
struct sockaddr_in localaddr,remoteaddr;
char in_buff[MESSAGE_LEN]={0,};
socketfd = socket(AF_INET,SOCK_STREAM, 0);
if(socketfd == -1){
std::cout<<"failed to create socket"<<std::endl;
exit(-1);
}
flags = fcntl(socketfd,F_GETFL,0);
fcntl(socketfd,F_SETFL,flags| O_NONBLOCK);
ret = setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(ret == -1){
std::cout<<"failed to set socket options"<<std::endl;
}
localaddr.sin_family = AF_INET;
localaddr.sin_port = PORT;
localaddr.sin_addr.s_addr = INADDR_ANY;
bzero(&(localaddr.sin_zero),8);
ret = bind(socketfd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));
if(ret == -1){
std::cout<<"fail to bind"<<std::endl;
exit(-1);
}
ret = listen(socketfd,backlog);
if(ret == -1){
std::cout<<"fail to listen"<<std::endl;
exit(-1);
}
epoll_fd = epoll_create(256);
ev.events = EPOLLIN;
ev.data.fd = socketfd;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socketfd,&ev);
std::cout<<"fork foreifn"<<std::endl;
for(int i=0;i< MAX_PROCESS;i++){
if(pid != 0){
pid = fork();
}
}
std::cout<<"fork end"<<std::endl;
if(pid == 0){
for(;;){
event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);
for(int i=0;i<event_number;i++){
if(events[i].data.fd == socketfd){
socklen_t addr_len = sizeof(struct sockaddr);
accept_fd = accept(socketfd,(struct sockaddr *) &remoteaddr,
&addr_len
);
flags = fcntl(accept_fd,F_GETFL,0);
fcntl(accept_fd,F_SETFL,flags| O_NONBLOCK);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = accept_fd;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);
}else if(events[i].events & EPOLLIN){
do{
memset(in_buff,0,MESSAGE_LEN);
ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
if(ret == 0){
close(events[i].data.fd);
}
if(ret == MESSAGE_LEN){
std::cout<< "maybe have more data"<<std::endl;
}}while(ret < 0 && errno == EINTR);
if(ret < 0){
switch(errno){
case EAGAIN:
std::cout<<"no hava more data..."<<std::endl;
break;
default:
break;
}
}
std::cout<<"rev:"<<in_buff<<std::endl;
send(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
}
}
}
close(socketfd);
}else{
do{
pid = waitpid(-1,NULL,0);
}while(pid != -1);
}
return 0;
}
启动后会看见这样几行代码,足以说明,fork的子进程的变量与父进程的是一样的
root@t:/opt/mediaserver# ./tcp_server
fork foreifn
fork end
fork end
fork end
fork end
fork end
更多推荐
已为社区贡献1条内容
所有评论(0)