一、相关函数说明
int epoll_create(int size);
函数说明:
创建接口,返回epoll的操作句柄
参数说明:
size:不能传递负数,传递整数的情况下参数无意义
调用示例:
epoll_fd_ = epoll_create(5);
epoll事件集合:
struct epoll_event
{
uint32_t eventes;
//epoll事件,EPOLLIN(可读事件),EPOLLOUT(可写事件)
epoll_data_t data;
//存放该事件结构对应的文件描述符或存放文件描述符结构的地址
}
epoll_data_t结构:
typedef union epoll_data
{
viod *ptr;//ptr指向的空间中要有fd的值
int fd;//设置为要监控的文件描述符
}epoll_data_t;
epoll_data_t是联合,成员变量(ptr/fd)公用内存,所以一次只使用一个保存文件描述符
typedef:为现有类型起个别名
int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event);
函数说明:
用于对文件描述符的增删改
参数说明:
epfd:epoll的操作句柄
op:
(1)EPOLL_CTL_ADD:添加一个文件描述符的事件结构到epoll当中
(2)EOPOLL_CTL_MOD:修改一个文件描述符对应的事件结构
(3)EPOLL_CTL_DEL:从epoll当中删除一个文件描述符对应的事件结构
fd:要被处理的文件描述符
event:文件描述符对应的事件结构
调用示例:
//添加一个文件描述符
struct epoll_event ee;
ee.events = EPOLLIN;
ee.data.fd = newsockfd;
epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, newsockfd, &ee);
int epoll_wait(int epfd,struct epoll_event* event,int maxevents,int timeout);
函数说明:
等待接口,获取就绪的文件描述符,返回就绪的文件描述符个数
参数说明:
epfd:epoll的操作句柄
events:事件结构集合,从epoll中获取就绪的事件结构(从内核的双向链表中拷贝。
maxevents:一次最多获取的事件结构
timeout:
(1)>0:带有超时事件
(2)==0:非阻塞
(3)<0:阻塞
调用示例:
struct epoll_event arr[10];
int ret = epoll_wait(cs->epoll_fd_, arr, sizeof(arr)/sizeof(arr[0]), -1);
epoll的工作模式
LT(epoll默认的触发方式):水平触发(只要达到触发事件的条件,就进行触发(通知),如果事件没有被处理完,不会进行新一轮监听,还会继续触发该事件)
ET:边缘触发(当达到事件触发条件,只会触发(通知)一次,需要设置非阻塞,搭配循环使用)
二、用epoll实现服务端和多个客户端的通信(tcp)
1.服务端
/*
* 1.创建侦听套接字
* 2.绑定地址信息
* 3.监听
* 4.获取新连接套接字
* 5.创建epoll操作句柄
* 6.添加侦听套接字
* 7.循环处理
* *调用epoll_wait
* *分情况处理根据套接字类型不同进行不同处理(侦听套接字, 新连接套接字)
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
int main(){
//创建侦听套接字
int listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sockfd < 0){
perror("socket");
return 0;
}
//绑定地址信息
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(39898);
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0){
perror("bind");
return 0;
}
//监听
listen(listen_sockfd, 5);
//创建接口,返回epoll操作句柄
int epfd = epoll_create(10);
if(epfd < 0){
perror("epoll_create");
return 0;
}
//将侦听套接字加入epoll
struct epoll_event ee;
ee.events = EPOLLIN;
ee.data.fd = listen_sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sockfd, &ee);
while(1){
//1. 调用epoll_wait,等待就绪的文件描述符
struct epoll_event arr[10];
int ret = epoll_wait(epfd, arr, sizeof(arr)/sizeof(arr[0]), -1);
if(ret < 0){
continue;
}
//2. 对就绪的文件描述符进行分情况处理(侦听套接字, 新连接套接字)
for(int i = 0; i < ret; i++){
if(listen_sockfd == arr[i].data.fd){
//处理侦听套接字
/*
* 1. accept接收新连接,返回新连接套接字
* 2. 添加新连接套接字到epoll当中
* */
int newsockfd = accept(arr[i].data.fd, NULL, NULL);
if(newsockfd < 0)
{
continue;
}
struct epoll_event eee;
eee.events = EPOLLIN;
eee.data.fd = newsockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, newsockfd, &eee);
}
else
{
//处理新连接套接字
char buf[1024] = {0};
size_t r_size = recv(arr[i].data.fd, buf, sizeof(buf) - 1, 0);
if(r_size < 0)
{
continue;
}
else if(r_size == 0)
{
/*对端将连接关闭了
* 1.从epoll当中移除该文件描述符
* 2.关闭文件描述符
*/
epoll_ctl(epfd, EPOLL_CTL_DEL, arr[i].data.fd, NULL);
close(arr[i].data.fd);
continue;
}
printf("[recv from %d, msg is ] %s\n", arr[i].data.fd, buf);
memset(buf, '\0', sizeof(buf));
snprintf(buf, sizeof(buf), "%d, i am server", arr[i].data.fd);
send(arr[i].data.fd, buf, strlen(buf), 0);
}
}
}
return 0;
}
2.客户端
/*客户端
*1.创建套接字
*2.发起连接
*3。收发数据
*/
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
//创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0)
{
perror("socket");
return 0;
}
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(38989);
addr.sin_addr.s_addr=inet_addr("127.0.0.1");
//发起连接
int ret=connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret<0)
{
perror("connect");
return 0;
}
while(1)
{
//收发数据
char buf[1024]={0};
printf("please enter:");
fflush(stdout);
std::cin>>buf;
send(sockfd,buf,strlen(buf),0);
memset(buf,'\0',1024);
ssize_t recv_size=recv(sockfd,buf,sizeof(buf)-1,0);
if(recv_size<0)
{
perror("recv");
return 0;
}
else if(recv_size==0)
{
close(sockfd);
return 0;
}
else
{
printf("buf is:%s\n",buf);
}
}
//关闭套接字
close(sockfd);
return 0;
}