7.10 第九章I/O复用高级函数 select poll epoll(lt et)
程序员文章站
2022-03-12 20:06:46
//所有内容均来自linux高性能服务器编程这本书.利用epoll中的EPOLLONESHOT实现一段时间内只能有一个线程在处理一个SOKCET 防止该线程刚读取完数据准备处理 又来了一个数据 启动另外一个线程来处理 ONESHOT只会触发一次 所以当该工作进程处理完当前的事件之后,应该重置EPOLL_CTL_MOD 该FD状态 以便于下一次能够再次触发#include #include #include
//所有内容均来自linux高性能服务器编程这本书.
利用epoll中的EPOLLONESHOT实现一段时间内只能有一个线程在处理一个SOKCET 防止该线程刚读取完数据准备处理 又来了一个数据 启动另外一个线程来处理 ONESHOT只会触发一次 所以当该工作进程处理完当前的事件之后,应该重置EPOLL_CTL_MOD 该FD状态 以便于下一次能够再次触发
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <stdbool.h>
// linux下使用结构体一定要在前面加入struct这个字 不然是没有效果的
#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 1024
// 由于之前没有什么敲linux下代码的经验
// 这次debug调式了很久的时间 需要注意的点就是如果是应用一个结构体 那么就需要在前面加入struct关键字 对于指针的传递一定要写&地址符号 (在实际编程中很少这样写 导致频繁出错) !!
typedef struct // 定义一个结构体
{
int epollfd;
int socket;
}fds;
int setnoblock(int fd)
{
int old_option = fcntl(fd,F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
}
void addfd(int epollfd,int fd,bool oneshot)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
if(oneshot)
{
event.events |= EPOLLONESHOT;
}
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnoblock(fd);
}
void reset_oneshot(int epollfd,int fd)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
}
void* work(void* arg) // 主线程已经帮我连接了 为只是负责处理这个事件
{
int sockfd = (( fds* )arg)->socket;
int epollfd = (( fds* )arg)->epollfd;
printf("start new thread to receive msg on %d\n",socket);
char buf[BUFFER_SIZE];
memset(buf,0, BUFFER_SIZE);
while(1)
{
int ret = recv(sockfd,buf,BUFFER_SIZE - 1,0);
if(ret == 0)
{
close(socket);
printf("foreiner close the connect\n");
break;
}
else if(ret < 0)
{
if(errno == EAGAIN)
{
reset_oneshot(epollfd,socket);
printf("all revc and reset socket");
break;
}
}
else
{
printf("the msg is %s\n",buf);
sleep(5); // 模拟处理过程
}
printf("end thread receiving data on fd: %d\n",socket);
}
}
int main()
{
const char* ip = "";
int port = 80;
struct sockaddr_in address;
bzero(&address,sizeof address);
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET,ip,&address.sin_addr);
int listenfd = socket(PF_INET,SOCK_STREAM,0);
assert(listenfd >= 0);
int ret = bind(listenfd,(struct sockaddr *)&address,sizeof address);
assert(ret != -1);
ret = listen(listenfd,5);
assert(ret != -1);
struct epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
addfd(epollfd,listenfd,false);
while(1)
{
int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(ret < 0)
{
printf("epoll failed");break;
}
for(int i = 0; i < ret ; i++)
{
int sockfd = events[i].data.fd;
if(sockfd == listenfd)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int t = accept(sockfd,(struct sockaddr*)&client,&len);
addfd(epollfd,t,true);
}
else if(events[i].events & EPOLLIN)
{
pthread_t thread;
fds fds_for_new_work;
fds_for_new_work.epollfd = epollfd;
fds_for_new_work.socket = events[i].data.fd;
pthread_create(&thread,NULL,work,(void *)&fds_for_new_work); // 传递给工作线程的函数
}
else
{
printf("someting else happen\n");
}
}
}
close(listenfd);
return 0;
}
利用I/O复用 可以实现的高级功能有:
1.非阻塞的connect
同时发出多个链接 。用select监听有无读写数据
2.聊天室程序
多个用户同时在线进行聊天 poll I/O复用实现多用户在线聊天
客户端程序:
3.同时处理TCP和UDP服务
本文地址:https://blog.csdn.net/qq_41468712/article/details/107254109