- Horizontal Trigger Mode--This is the default mode (as described in the previous article)
- Edge Blocking Trigger Mode
- Edge Non-Blocking Mode--Highest Efficiency
Let's start with a need:
For one client (interprocess pipeline communication) for one server
If the client sends 100 bytes of information and the server receives only 50 bytes at a time, what about the remaining 50 bytes?
Analysis:
- Default Execution Flow: The corresponding buffer holds 100 bytes sent, the system epoll listens for changes in the corresponding file descriptor, the server reads the data, but only reads 50 bytes, then there are 50 bytes left in the buffer
There are two statements at this point: Fact is the second
1. To improve efficiency, the epoll_wait function will not be called anymore, then 50 bytes of data will only be received when the next client sends information (to improve efficiency, reduce the number of calls to the function)
2. The epoll_wait function will be called again to read the data. (Code will be attached later) - Edge Blocking Trigger Mode: Like the first scenario above, but it causes data to remain in the buffer every time, and more and more...
- Edge non-blocking (O_NONBLOCK) trigger mode: This is most efficient mainly because the file descriptor corresponding to the client, buffer (pipeline), is set to non-blocking mode. When receiving (reading) information, it needs to be read in a loop. When read/recv returns 0, it means the read is complete. In addition, edge mode (calling epoll_wait function only once) is efficient
Set non-blocking:
1. Set parameters when opening;
2.fcntl settings
//Modify file attributes after opening the file to win the set attributes flags //Get flags: int flags = fcntl(fd, F_GETFL); //Set flags: flags = flags | O_NONBLOCK; fcntl(fd, F_SETFL, flags);
Code for the three modes:
- The first two modes are implemented using pipe-to-parent process communication:
Toggle where comments are made in the code, and the output is formatted as described above, where 10 and 5 bytes are simulated
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #include <error.h> int main(int argc, char *argv[]) { char buf[10]; //Using pipeline implementations, pipeline creation requires an array, one for reading and one for writing int pfd[2]; //Create anonymous pipe pipe(pfd); //Create Subprocess pid_t pid = fork(); if(pid == 0) { //Subprocess //No read operation is required, close the read file descriptor to ensure that the pipeline transmits data individually close(pfd[0]); while(1) { int i = 0; for(i = 0; i < 10/2; i++) { buf[i] = 'a'; } buf[i-1] = '\n'; for(; i < 10; i++) { buf[i] = 'b'; } buf[i-1] = '\n'; //The array now holds aaaa\nbbn //Send 10 bytes at a time write(pfd[1], buf, sizeof(buf)); sleep(3); } close(pfd[1]); } else if(pid > 0) {//Parent Process close(pfd[1]); //Create epoll model, point to root node, handle int efd = epoll_create(10); //Mount the monitor on the root node struct epoll_event event; //Setting the edge triggers as follows: event.events = EPOLLIN | EPOLLET; /* * //The default is to trigger horizontally * event.events = EPOLLIN; */ event.data.fd = pfd[0];//Writing end epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event); struct epoll_event resevents[10]; char readbuf[5]; while(1) { int res = epoll_wait(efd, resevents, 10, -1); printf("res:%d\n", res); if(resevents[0].data.fd == pfd[0]) { int len = read(pfd[0], readbuf, 5);//Read 5 bytes at a time write(STDOUT_FILENO, readbuf, len); } } close(pfd[0]); close(efd); } else { perror("fork error"); exit(1); } return 0; }
- Edge blocking trigger implemented with c/s model, what we do here is listen on only one client
#include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/epoll.h> #include <unistd.h> #define MAXLINE 10 #define SERV_PORT 9000 int main(void) { struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 20); struct epoll_event event; struct epoll_event resevent[10]; int res, len; printf("Accepting connections ...\n"); cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); // Create a red-black tree root node int efd = epoll_create(10); // Detected Event Settings #if 0 /* ET Edge Trigger */ event.events = EPOLLIN | EPOLLET; #else /* Default LT Horizontal Trigger */ event.events = EPOLLIN; #endif // File descriptors that need to be detected event.data.fd = connfd; epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); while (1) { res = epoll_wait(efd, resevent, 10, -1); printf("========res %d\n", res); if (resevent[0].data.fd == connfd) { len = read(connfd, buf, MAXLINE/2); write(STDOUT_FILENO, buf, len); } } return 0; }
- Just like 2, only listen on the client, but here's what to note is that you can no longer tell if the client disconnected or failed to read or what you read based on the return value of read. You need to think about designing programs, such as using the void * pointer inside the data to store a time, disconnecting if you don't connect for a long time, or using the heartbeat package.
#include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/epoll.h> #include <unistd.h> #include <fcntl.h> #define MAXLINE 10 #define SERV_PORT 8000 int main(void) { struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 20); /////////////////////////////////////////////////////////////////////// struct epoll_event event; struct epoll_event resevent[10]; int efd = epoll_create(10); //event.events = EPOLLIN; printf("Accepting connections ...\n"); cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); /* Modify connfd for non-blocking read */ int flag = fcntl(connfd, F_GETFL); flag |= O_NONBLOCK; fcntl(connfd, F_SETFL, flag); /* ET Edge trigger, default is horizontal trigger */ event.events = EPOLLIN | EPOLLET; event.data.fd = connfd; //Add connfd to listen on red and black trees epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); while (1) { int len = 0; printf("epoll_wait begin\n"); //Up to 10, blocking listening int res = epoll_wait(efd, resevent, 10, -1); printf("epoll_wait end res %d\n", res); if (resevent[0].data.fd == connfd) { // Non-blocking read, polling // epoll_wait is triggered once, remaining data is read in a loop while ((len = read(connfd, buf, MAXLINE/2)) >0 ) { write(STDOUT_FILENO, buf, len); } } } return 0; }
Author: dab61956e53d
Link: https://www.jianshu.com/p/59ab5ecbaa76
Source: Short Book
Copyright belongs to the author.For commercial reprinting, please contact the author for authorization. For non-commercial reprinting, please indicate the source.