Recently implemented Bayesian classification algorithm with C + +, made a small tool to automatically identify spam information. There is a feature in the tool that communicates with the client by binding the specified port. The service side uses the Epoll network model. At the time of testing, it is found that the client and server communication is normal in the case of single user. However, in the case of multiple users concurrency, the client and the server end communication is not normal. At this point, the client can be a normal link to send data, but has been stuck in the receiving part of the data. The following figure:
This problem arises because of the improper use of the ET (edge-trigger) mode in Epoll. The code is as follows:
01 |
/************************************************** |
02 |
Function Name: Acceptconn |
03 |
Function: Accept the client's link |
04 |
Parameters: srvfd: Listening socket |
05 |
***************************************************/ |
06 |
void Acceptconn (int srvfd) |
08 |
struct sockaddr_in sin; |
09 |
socklen_t len = sizeof (struct sockaddr_in); |
12 |
int CONFD = Accept (srvfd, (struct sockaddr*) &sin, &len); |
16 |
printf ("%s:bad accept\n"); |
20 |
printf ("Accept Connection:%d", CONFD); |
23 |
Setnonblocking (CONFD); |
25 |
Add a newly established connection to the Epoll monitor |
26 |
struct Epoll_event event; |
27 |
EVENT.DATA.FD = CONFD; |
28 |
Event.events = epollin| Epollet; |
29 |
Epoll_ctl (EPOLLFD, Epoll_ctl_add, CONFD, &event); |
Note The penultimate line: event.events = epollin| Epollet; It uses the ET mode. Here we come to a specific point, the problem is there.
There are two modes in Epoll: Level-trigger mode, lt mode, and Edge-trigger mode, or et mode. In which, LT is the default mode of work.
The two modes work in a somewhat different way. In Level-trigger mode, whenever a socket is in a readable/writable state, the socket will be returned whenever epoll_wait , and writable returns the socket only if a socket changes from unreadable to readable or from unwritable to epoll_wait in Edge-trigger mode.
In the case of the ET mode socket non-blocking (This is the case in the code above), when multiple connections arrive at the same time, the TCP ready queue of the server accumulates multiple ready connections instantly, because it is an edge trigger mode, Epoll only notifies once, accept only one connection is processed, Causes the remaining connections in the TCP ready queue to not be processed. As a result, the issues mentioned above have arisen.
The solution is to hold the accept call with a while loop, finish all connections in the TCP ready queue, and then exit the loop. How do I know if I've finished processing all the connections in the ready queue? Accept returns-1 and errno set to Eagain means that all connections are processed.
The modified code is as follows:
01 |
/************************************************** |
02 |
Function Name: Acceptconn |
03 |
Function: Accept the client's link |
04 |
Parameters: srvfd: Listening socket |
05 |
***************************************************/ |
06 |
void Acceptconn (int srvfd) |
08 |
struct sockaddr_in sin; |
09 |
socklen_t len = sizeof (struct sockaddr_in); |
12 |
while ((CONFD = Accept (srvfd, (struct sockaddr*) &sin, &len)) > 0) { |