当前位置:网站首页>Network learning (III) -- highly concurrent socket programming (epoll)
Network learning (III) -- highly concurrent socket programming (epoll)
2022-07-07 08:04:00 【Literary youth】
Catalog
One 、 introduction
Two 、 High concurrency background
------> 2.1、select
3、 ... and 、epoll
------> 3.1、 Functional separation
------> 3.2、 Ready list
------> 3.3、 Code explanation
------> 3.4、 Process summary
One 、 introduction
The previous chapter talked about socket Programming for , This chapter introduces the relevant mechanisms of high concurrency servers
The original text refers to this article to a large extent Epoll Principle analysis
Two 、 High concurrency background
Let's review the last chapter socket Programming , The link between the host and the server is successful , After starting communication , Host calls recv() To get data , When the program runs to Recv when , It will always wait , Do not execute until data is received
One Socket It corresponds to a port number , And the network packet contains IP And port information , The kernel can find the corresponding... Through the port number Socket.
that , How to monitor multiple Socket Well ? This is what this chapter is about
The server needs to manage multiple client connections , and Recv You can only monitor a single Socket, In this contradiction , People began to look for surveillance of multiple Socket Methods .Epoll The point is to effectively monitor multiple Socket. And in the epoll Before , What has been used is select and pol Mechanism , The two are very similar , That's all select 了
1、select
In the code below , Prepare an array first FDS, Give Way FDS Store all the things that need to be monitored Socket.
And then call Select, If FDS All in Socket No data ,Select It will block , Until there is one Socket Data received ,Select return , Wake-up process .
Users can traverse FDS, adopt FD_ISSET Decide which one Socket Receive the data , And then deal with it .
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int fds[] = Store what needs to be monitored socket
while(1){
int n = select(..., fds, ...)
for(int i=0; i < fds.count; i++){
if(FD_ISSET(fds[i], ...)){
//fds[i] Data processing of
}
}
}
Select The process of
Select It's very direct , If the program simultaneously monitors Sock1、Sock2 and Sock3 Three Socket, So in calling Select after
1、 The operating system puts the process A Add these three Socket Waiting in the queue .
2、 When any one Socket After receiving the data , Interrupting the program will call up the process . The so-called arousal process , Is to remove the process from all the waiting queues , Join the work queue
3、 Through these steps , When a process A After being awakened , It knows at least one Socket Received data . The program only needs to traverse once Socket list , You can get ready Socket.
select The shortcomings of
This simple way works , In almost all operating systems there are corresponding implementations . But simple methods often have disadvantages , Mainly :
1、 Secondary call Select All processes need to be added to all monitoring Socket Waiting queue , Every wake-up needs to be removed from each queue . This involves two iterations , And every time I have to put the whole FDS List passed to kernel , There is a certain cost . It's because of the overhead of traversal , For the sake of efficiency , Will stipulate that Select The maximum number of monitors , By default, you can only monitor 1024 individual Socket.
2、 After the process is awakened , The program doesn't know what Socket Receive the data , You need to go through it again .
In order to solve these shortcomings , It's introduced epoll Mechanism
3、 ... and 、epoll
Epoll Is in Select appear N It was invented years later , yes Select and Poll(Poll and Select Is essentially the same , There are a few improvements ) Enhanced version of .Epoll Improve efficiency through the following measures :
1、 Functional separation
Select One of the reasons for the inefficiency is to “ Maintain waiting queues ” and “ Blocking process ” The two steps merge into one .
comparison Select,Epoll Split the function
As shown in the figure above , Every time you call Select Both of these two steps are needed , However, in most application scenarios , Need to be monitored Socket Relatively fixed , It doesn't need to be changed every time .
Epoll Separate these two operations , First use epoll_ctl Maintain waiting queues , Call again epoll_wait Blocking process . Obviously , Efficiency can be improved .
For the convenience of understanding the following content , Let's get to know Epoll Usage of . In the following code , First use epoll_create Create a Epoll object Epfd, Re pass epoll_ctl Will need to be monitored Socket Add to Epfd in , Last call epoll_wait Waiting for data :
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); // All that needs to be monitored socket Add to epfd in
while(1){
int n = epoll_wait(...)
for( Receiving data socket){
// Handle
}
}
2、 Ready list
Select Another reason for inefficiency is that the program doesn't know what Socket Receive the data , It can only be traversed one by one . If the kernel maintains a “ Ready list ”, Quote... From received data Socket, You can avoid traversal .
When a process calls epoll_create When the method is used , The kernel will create a eventpoll object ( In the process Epfd Represented by ).
establish Epoll After the object , It can be used epoll_ctl Add or remove the Socket. If you pass epoll_ctl add to Sock1、Sock2 and Sock3 Surveillance , The kernel will eventpoll Add to these three Socket Waiting in the queue .
When Socket After receiving the data , The interrupt program will operate eventpoll object , Instead of operating the process directly , At the same time, interrupting the program will give eventpoll Of “ Ready list ” add to Socket quote .
eventpoll The object is equal to Socket And the intermediary between the process ,Socket Data receiving does not directly affect the process , But by changing eventpoll To change the state of the process .
When the program is executed to epoll_wait when , If Rdlist Has quoted Socket, that epoll_wait Go straight back to , If Rdlist It's empty , Blocking process .
epoll_wait When blocking a process, the kernel will block the process A Put in eventpoll Waiting in the queue , Blocking process .
When Socket Data received , On the one hand, the interrupt program is modified Rdlist, On the other hand, wake up eventpoll Wait for the process in the queue , process A Enter the operation state again
Epoll Wake up process also because Rdlist The existence of , process A You can know what Socket There is a change .
3、 Code explanation
int main(int argc, char* argv[])
{
int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber;
ssize_t n;
char line[MAXLINE];
socklen_t clilen;
......
// Statement epoll_event Structural variables ,ev Used to register Events , Arrays are used to return events to be processed
struct epoll_event ev,events[20];
// Generate for processing accept Of epoll Dedicated file descriptors
epfd=epoll_create(256);
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
// Set the file descriptor associated with the event to be processed
ev.data.fd=listenfd;
// Set the type of event to process
ev.events=EPOLLIN|EPOLLET;
//ev.events=EPOLLIN;
// register epoll event
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
char *local_addr="127.0.0.1";
inet_aton(local_addr,&(serveraddr.sin_addr));//htons(portnumber);
serveraddr.sin_port=htons(portnumber);
bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ);
maxi = 0;
for ( ; ; ) {
// wait for epoll The occurrence of the incident
nfds=epoll_wait(epfd,events,20,500);
// Deal with all the events that happen
for(i=0;i<nfds;++i)
{
if(events[i].data.fd==listenfd)// If a new one is detected SOCKET The user is connected to the bound SOCKET port , Establish a new connection .
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
if(connfd<0){
perror("connfd<0");
exit(1);
}
//setnonblocking(connfd);
char *str = inet_ntoa(clientaddr.sin_addr);
cout << "accapt a connection from " << str << endl;
// Set the file descriptor for the read operation
ev.data.fd=connfd;
// Set the read operation event used for annotation test
ev.events=EPOLLIN|EPOLLET;
//ev.events=EPOLLIN;
// register ev
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
}
else if(events[i].events&EPOLLIN)// If it's a connected user , And receive data , So read in .
{
cout << "EPOLLIN" << endl;
if ( (sockfd = events[i].data.fd) < 0)
continue;
if ( (n = read(sockfd, line, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
close(sockfd);
events[i].data.fd = -1;
} else
std::cout<<"readline error"<<std::endl;
} else if (n == 0) {
close(sockfd);
events[i].data.fd = -1;
}
line[n] = '/0';
cout << "read " << line << endl;
// Set file descriptors for write operations
ev.data.fd=sockfd;
// Set the write operation event for annotation test
ev.events=EPOLLOUT|EPOLLET;
// modify sockfd The event to be handled on is EPOLLOUT
//epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
else if(events[i].events&EPOLLOUT) // If there's data to send
{
sockfd = events[i].data.fd;
write(sockfd, line, n);
// Set the file descriptor for the read operation
ev.data.fd=sockfd;
// Set the read operation event used for annotation test
ev.events=EPOLLIN|EPOLLET;
// modify sockfd The event to be handled on is EPOLIN
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
}
}
return 0;
}
4、 Process summary
The main process is as follows
1、epoll_create Create for processing accept Of epoll Dedicated file descriptors
2、 Create a listener socket, Put it in the epoll_event File descriptor to be processed in , And fill in Event type , among EPOLLLT and EPOLLET The difference between
3、 after bind、listen Start listening for connection sockets
4、epoll_wait First judge epoll_event File descriptors in , If it is a listening descriptor , It means that a new connection has occurred ,accept A new socket , Refill epoll_event , Reuse epoll_ctl Sign up to epoll In the descriptor of
5、epoll_wait If it's a connected user , Judge epoll_wait type (EPOLLIN/EPOLLOUT), Respectively called read、write
边栏推荐
- Recursive method to construct binary tree from preorder and inorder traversal sequence
- Installing postgresql11 database under centos7
- Binary tree and heap building in C language
- 贝叶斯定律
- 2022 Inner Mongolia latest advanced fire facility operator simulation examination question bank and answers
- A bit of knowledge - about Apple Certified MFI
- 2022制冷与空调设备运行操作复训题库及答案
- OpenJudge NOI 2.1 1752:鸡兔同笼
- Niu Mei's mathematical problem --- combinatorial number
- Li Kou interview question 04.01 Path between nodes
猜你喜欢
[matlab] when matrix multiplication in Simulink user-defined function does not work properly, matrix multiplication module in module library can be used instead
央视太暖心了,手把手教你写HR最喜欢的简历
2022焊工(初级)判断题及在线模拟考试
The charm of SQL optimization! From 30248s to 0.001s
Detailed explanation of Kalman filter for motion state estimation
LeetCode中等题之我的日程安排表 I
Content of string
JS quick start (I)
【数字IC验证快速入门】14、SystemVerilog学习之基本语法1(数组、队列、结构体、枚举、字符串...内含实践练习)
自定义类加载器加载网络Class
随机推荐
Li Kou interview question 04.01 Path between nodes
Thinkcmf6.0安装教程
Cnopendata geographical distribution data of religious places in China
UnityHub破解&Unity破解
Pytorch(六) —— 模型调优tricks
[matlab] when matrix multiplication in Simulink user-defined function does not work properly, matrix multiplication module in module library can be used instead
OpenJudge NOI 2.1 1752:鸡兔同笼
Qt学习26 布局管理综合实例
[Matlab] Simulink 自定义函数中的矩阵乘法工作不正常时可以使用模块库中的矩阵乘法模块代替
Installing postgresql11 database under centos7
What is the interval in gatk4??
LeetCode 90:子集 II
自定义类加载器加载网络Class
PHP exports millions of data
C语言航班订票系统
【数字IC验证快速入门】10、Verilog RTL设计必会的FIFO
力扣(LeetCode)187. 重复的DNA序列(2022.07.06)
Linux server development, MySQL cache strategy
JS quick start (I)
Custom class loader loads network class