代码基本框架参考:https://blog.51cto.com/14569275/2538214
背景
能确切指定有数据的socket
线程安全
结构体
结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;//保存触发事件的某个文件描述符相关的数据
struct epoll_event {
__uint32_t events; /* epoll event */
epoll_data_t data; /* User data variable */
};
函数
创建
int epoll_create(int size)
参数:size监听的数目
返回值: 文件描述符 可以在/proc/PID/fd查看
控制
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
参数:
操作:
返回值:
轮询I/O事件
int epoll_wait(int epfd,struct epoll_event events,int maxevents,int timeout)
参数:
返回值:
编码流程
1.创建epoll描述符
2.注册epoll事件
3.等待epoll事件
4.判断触发epoll事件的描述符和事件
5.关闭epoll描述符
触发条件
客户端代码
#include<iostream>
#include<arpa/inet.h>
#nclude<unistd.h>
#include<sys/epoll.h> //epoll
#include<linux/fs.h> //OPEN_MAX
#include<strings.h>
using namespace std;
string name;
void show_connect(int fd){
//获取本地址和端口
struct sockaddr_in local_addr;
socklen_t local_addr_len=sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout<<"local"<<inet_ntoa(local_addr.sin_addr)<<":"<<ntohs(local_addr.sin_port)<<endl;
//获取远程地址和端口
struct sockaddr_in remote_addr;
socklen_t remote_addr_len=sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout<<"remote"<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
}
//./client ip port
int main(int argc,char* argv[]){
if(argc!=4){
cout<<"usage:"<<argv[0]<<"Ip port name"<<endl;
return 1;
}
name=argv[3];
//1.创建套接字
int connfd=socket(AF_INET,SOCK_STREAM,0);
if(connfd==-1){
cout<<" socket error"<<endl;
return 1;
}
//2连接服务器
struct sockaddr_in remote_addr;
remote_addr.sin_family=AF_INET;//协议
remote_addr.sin_addr.s_addr=inet_addr(argv[1]);//IP地址
remote_addr.sin_port=htons(atoi(argv[2]));//端口号
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
cout<<"connect error"<<endl;
return 1;
}else{
cout<<"connect success"<<endl;
show_connect(connfd);
}
//创建epoll描述符
int epollfd=epoll_create(2);
//注册事件
struct epoll_event evt;
evt.data.fd=STDIN_FILENO;
evt.events=EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt);
evt.data.fd=connfd;
evt.events=EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);
bool stop=false;
while(!stop){
int count=2;
struct epoll_event revt[2];
int revt_count=epoll_wait(epollfd,revt,count,-1);
for(int i=0;i<revt_count;i++){
if(revt[i].data.fd==STDIN_FILENO &&revt[i].events & EPOLLIN){
//发送消息
string message;
getline(cin,message);
message=name+":"+message;
write(connfd,message.c_str(),message,szie()+1);
}else if(revt[i].data.fd==connfd & revt[i].events & EPOLLIN){
//接收数据
char buffer[1024]={0};
int len=read(connfd,buffer,sizeof(buffer));
if(len==0){
cout<<"server exit"<<endl;
epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
--count;
stop=true;
break;
}else{
cout<<buffer<<endl;
}
}
}
}
close(epollfd);
close(connfd);
return 0;
}
服务器端
#include<iostream>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<linux/fs.h> //INR_OPEN_MAX
#nclude<unistd.h>
#include<strings.h>
using namespace std;
void show_connect(int fd){
struct sockaddr_in local_addr;
socklen_t local_addr_len=sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout<<"local"<<inet_ntoa(local_addr.sin_addr)<<":"<<ntohs(local_addr.sin_port)<<endl;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len=sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout<<"remote"<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
}
void Show(struct pollfd* fds,int n){
for(int i=0;i<n;i++){
cout<<fds[i].fd<<" ";
}
cout<<endl;
}
./server ip port
int main(int argc,char* argv[]){
cout<<FD_SETSIZE<<endl;
if(argc!=3){
cout<<"usage:"<<argv[0]<<"Ip port name"<<endl;
return 1;
}
//1.监听套接字
int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd==-1){
cout<<"listen socket error"<<endl;
return 1;
}
//设置端口重复利用
int flag=1; setsockopt(listenfd,SQL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
//2.绑定
struct sockaddr_in local_addr;
local_addr.sin_family=AF_INET;//协议
local_addr.sin_addr.s_addr=inet_addr(argv[1]);//IP地址
local_addr.sin_port=htons(atoi(argv[2]));//端口号
if(-1==bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))){
cout<<"bind error"<<endl;
return 1;
}else{
cout<<"bind success"<<endl;
}
//3.监听设置
if(listen(listenfd,10)==-1){
cout<<"listen error"<<endl;
return 1;
}else{
cout<<"listen success"<<endl;
}
int epollfd=epoll_create(INR_OPEN_MAX);
struct epoll_event evt;
evt.data.fd=STDIN_FILENO;
evt.events=EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt);
evt.data.fd=listenfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&evt);
int count=2;
list<int> fds;
while(true){
struct epoll_event revt[count];
int revt_cnt=epoll_wait(epollfd,revt,count,-1);
for(int i=0;i<revt_cnt;i++){
if(revt[i].data.fd==STDIN_FILENO & revt[i].events & EPOLLIN){
string message;
getline(cin,message);
cin.clear(); //清空输入出错
if(!message.empty()){
message="广告"+message;
for(auto fd:fds){
write(fd,message.c_str(),message.size()+1);
}
}
}else if(revt[i].data.fd==listenfd && revt[i].events & EPOLLIN){
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len=sizeof(remote_addr);
int connfd=accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
if(connfd==-1){
cout<<"accept error"<<endl;
return 1;
}else{
cout<<"accept success"<<endl;
cout<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
show_connect(connfd);
struct epoll_event evt;
evt.data.fd=connfd;
evt.events=EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);
fds.push_back(connfd);
++count;
}
}else{
int connfd=revt[i].data.fd;
char buffer[1024]={0};
int n=read(connfd,buffer,1024);//读取客户端发过来的数据
if(n==0){
cout<<"client exit"<<endl;
close(connfd);//关掉退出连接
epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
fds.remove(connfd);
--count;
break;
}else{
for(auto fd:fds){//发送消息给所有客户端
if(fd==connfd) continue;
write(fd,buffer,1024);
}
}
}
}
}
close(epollfd);
close(listenfd)
}
汇总比较
select IO多路复用
1.只能监视FD_SETSIZE个连接
2.不能确切指定有数据的socket
3.每次需要修改传入的fd_set
4.线程不安全
Poll IO多路复用
优点
1.不需要不修改传入的pollfd数组
2.可以监视任意个连接
缺点
1.不能确切指定有数据的socket
2.线程不安全
Epoll IO多路复用
优点
1.能确切指定有数据的socket
2.线程安全
原文:https://blog.51cto.com/14569275/2538607