nicht blockierenden Socket recv Problem, während es mit epoll mit
-
01-10-2019 - |
Frage
ich habe ein Problem: manchmal (nicht regelmäßig) recv kehrt -1
und errno == EAGAIN
während epoll
in edge-triggered
Modus. Stück Code:
server_sock = startup(&port);
if ( (epollfd = epoll_create(4096)) < 0) {
perror("epoll_create error");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = server_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &ev) == -1) {
perror("epoll_ctl: server_sock");
exit(EXIT_FAILURE);
}
while (1) {
int nfds = epoll_wait(epollfd, events, 4096, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == server_sock) {
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
(socklen_t *)(&client_name_len));
if (client_sock == -1) //server overloaded
continue;
if (events[i].events & EPOLLIN) {
std::cout << "EPOLLIN on " << client_sock << std::endl;
}
Arch::set_nonblocking(client_sock);
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; //input data and connection closing
ev.data.fd = client_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &ev) == -1) {
perror("epoll_ctl: client_socket");
exit(EXIT_FAILURE);
}
accept_request(client_sock);
} else {
if (events[i].events & EPOLLRDHUP) {
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
}
}
}
}
startup(&port)
schafft nicht blockierenden Socket, Bindung mit Port und so weiter. mein Skript sendet folgende Daten:
GET /connect?id=1&secret=1 HTTP/1.0\r\n\r\n
aber manchmal recv
Rückkehr -1
in dieser Funktion (innerhalb von accept_request
Aufruf):
/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
* carriage return, or a CRLF combination. Terminates the string read
* with a null character. If no newline indicator is found before the
* end of the buffer, the string is terminated with a null. If any of
* the above three line terminators is read, the last character of the
* string will be a linefeed and the string will be terminated with a
* null character.
* Parameters: the socket descriptor
* the buffer to save the data in
* the size of the buffer
* Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size) {
int i = 0;
char c = '\0';
int n;
while ((i < size - 1) && (c != '\n')) {
n = recv(sock, &c, 1, 0);
//debug
std::cout << "n = " << n << std::endl;
if (n > 0) {
if (c == '\r') {
n = recv(sock, &c, 1, MSG_PEEK);
if ((n > 0) && (c == '\n'))
recv(sock, &c, 1, 0);
else
c = '\n';
}
buf[i] = c;
i++;
} else {
//debug
if (errno == EWOULDBLOCK)
std::cout << "EWOULDBLOCK" << std::endl;
c = '\n';
}
}
buf[i] = '\0';
return(i);
}
als epoll man-Seite schrieb ich gelesen habe, zu / Schreib bis ich EAGAIN
bekommen, aber ich habe es schon! und ich sicher, dass der Puffer nicht leer sein. was ich falsch gemacht?
UPD: Ich habe eine interessante Sache herausgefunden: wenn eine solche Situation passiert ist verwende ich in meinem Code sleep(1)
und recc(...)
wieder und ich bekomme Daten, die ich erwarten! es ist ein schmutziger Trick. Gibt es einen anmutigen Ansatz, um dieses Problem zu lösen?
Lösung
Es ist völlig normal, dass der erste recv()
in diesem Fall auf Rückkehr EAGAIN
. epoll()
habe nie gesagt, wenn es lesbar noch oder nicht.
Jedes einzelne recv()
sollte Griff EAGAIN
hergestellt werden, wenn Sie mit nicht-blockierende Sockets. Unechte Wakeups sind möglich, so, wenn eine API wie select()
, poll()
oder epoll()
Ihnen sagt, dass ein Socket lesbar ist, wird es nur zu sagen „es könnte lesbar sein - probieren Sie es aus“