epoll实现IO多路转接服务器
可通过以下视频学习
06-opell函数实现的多路IO转接_哔哩哔哩_bilibili
通过响应式--多路IO转接实现
文章目录
1.思路&功能
**功能:**客户端输入小写字符串,服务器转成大写返回给客户端
思路:
核心思路
- socket()、bind()、listen()
- epoll_create创建红黑树,它的返回值就是树的根节点
- epoll_ctl将listenfd添加到树上
- 循环epoll_wait进行监听,它的返回值是满足监听的总个数,所以以它的返回值为遍历上限去判断事件
- 如果它返回的数组中data.fd等于lfd,那么就accept去连接客户端 并将新的cfd加入树中
- 如果不是lfd,就说明有读事件发生,就去判断读到的返回值,<0是出错 ==0是客户端关闭(这两个都要去将该cfd从树中移除 并close),>0就处理数据然后写回
2.代码实现
multi_epoll_sever.c
c
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define OPEN_MAX 5000
#define SERVE_PORT 9527
int main()
{
// 所需要的变量
int lfd, cfd, efd, ret, wait_ret, i, sockfd, len;
char buf[1024];
// 地址结构体
struct sockaddr_in serve_addr, client_addr;
socklen_t client_addr_len;
serve_addr.sin_family = AF_INET; // IPV4
serve_addr.sin_port = htons(SERVE_PORT); // 绑定端口
serve_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定ip(ANY系统自动分配)
// 创建socket
lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd < 0)
{
perror("socket error");
exit(1);
}
// bind绑定
bind(lfd, (struct sockaddr *)&serve_addr, sizeof(serve_addr));
// 设置上限
listen(lfd, 128);
// 创建红黑树
efd = epoll_create(1); // efd就是树的根节点
// 将lfd挂在树上
// epoll结构体 ep是epoll_wait所需的数组(存放满足事件的fd)
struct epoll_event tep, ep[128]; // tep是epoll_ctl的参数(传监听的事件)
tep.events = EPOLLIN;
tep.data.fd = lfd;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);
if (ret < 0)
{
perror("epoll_ctl error");
exit(1);
}
// 循环去epoll_wait进行监听
while (1)
{
wait_ret = epoll_wait(efd, ep, 128, -1); // wait_ret就是实际满足事件的总个数
// 以wait_ret为上限去遍历事件
for (i = 0; i < wait_ret; i++)
{
// sockfd用于接收满足事件的fd
sockfd = ep[i].data.fd;
// 如果等于lfd,那就说明有客户端要来连接,就去accept
if (sockfd == lfd)
{
client_addr_len = sizeof(client_addr);
cfd = accept(lfd, (struct sockaddr *)&client_addr, &client_addr_len);
// 将cfd设置为非阻塞
int flag = fcntl(cfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag);
// 把新的cfd加入树中
tep.events = EPOLLIN | EPOLLET;
tep.data.fd = cfd;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);
if (ret < 0)
{
perror("epoll_ctl cfd error");
exit(1);
}
}
// 如果不是lfd,那就说明有读事件发生(读数据)
else
{
len = read(sockfd, buf, sizeof(buf));
if (len == 0) // 说明对方关闭连接(从树上摘下 & close)
{
epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
close(sockfd);
}
else if (len == -1)
{
perror("read error");
exit(1);
}
else // 读写数据
{
for (i = 0; i < len; i++)
buf[i] = toupper(buf[i]);
write(sockfd, buf, len);
write(STDIN_FILENO, buf, len);
}
}
}
}
return 0;
}
gcc
gcc multi_epoll_sever.c -o multi_epoll_sever
cpp
//也可以使用和其他多路IO一样的封装的warp.h warp.c的版本
//就不贴warp.c和warp.c了,大家有需要可以去select和poll的博客中查看
#include"warp.h"
#define SERV_PORT 9527
#define OPEN_MAX 20
int main(int argc,char * argv[])
{
int i,listenfd,connfd,efd,res,nready,sockfd;
int n,num=0;
char buf[4096],str[INET_ADDRSTRLEN];
socklen_t clien;
listenfd=Socket(AF_INET,SOCK_STREAM,0);
int opt=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in servaddr,cliaddr;
struct epoll_event tep,ep[OPEN_MAX];
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERV_PORT);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
Listen(listenfd,128);
efd=epoll_create(OPEN_MAX);
if(efd==-1)
perr_exit("epoll_create error");
tep.events=EPOLLIN;
tep.data.fd=listenfd;
res=epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);
if(res==-1)
perr_exit("epoll_ctl error");
for(;;)
{
nready=epoll_wait(efd,ep,OPEN_MAX,-1);
if(nready==-1)
perr_exit("epoll_wait error");
for(i=0;i<nready;i++)
{
if(!ep[i].events&EPOLLIN)
continue;
if(ep[i].data.fd==listenfd)
{
clien=sizeof(cliaddr);
connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&clien);
printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
printf("cfd %d---client %d\n",connfd,++num);
tep.events=EPOLLIN;
tep.data.fd=connfd;
res=epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);
if(res==-1)
perr_exit("epoll_ctl error");
}
else
{
sockfd=ep[i].data.fd;
n=Read(sockfd,buf,4096);
if(n==0)
{
res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
if(res==-1)
perr_exit("epoll_ctl error");
Close(sockfd);
printf("client[%d] closed connection\n",sockfd);
}
else if(n<0)
{
perror("read n<0 error:");
res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
Close(sockfd);
}
else
{
for(i=0;i<n;i++)
buf[i]=toupper(buf[i]);
Write(STDOUT_FILENO,buf,n);
Write(sockfd,buf,n);
}
}
}
}
Close(listenfd);
Close(efd);
return 0;
}
运行图
两个客户端访问服务器端