Timeout bei send/recv geht nicht
-
Hallo Community,
Ich habe ein Proxy-prüf-Programm geschrieben. Es versucht einfach über einen Proxy eine Seite abzurufen und gibt mir das Ergebnis aus.
Das Problem ist, dass manche Proxies so lahm sind, dass diese Abfrage mehrere Minuten dauert. Da will ich nun ein Timeout haben.
Ich habe schon folgendes probiert:setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
Scheint aber nicht zu funktionieren. In der Referenz zu den optionen steht auch, dass man diese zwar setzen kann, sie aber keine Auswirkungen haben.
Welche Möglichkeiten gibt es denn noch ein Timeout für die Abfrage zu realisieren?
(Es ist übrigens ein Multithreading-Programm, daher kann ich das Alarm-Signal nicht nutzen)
-
Du kannst bei poll(2) einen Timeout angeben, und damit pruefen, ob ein connect(2) abgeschlossen ist. Dazu musst Du den Socket mittels fcntl(2) auf O_NONBLOCK setzen.
-
Das connect-Timeout habe ich ja drin, das funktioniert:
con = connect(sock, (struct sockaddr *)&sin, sizeof(sin)); if(con < 0) { if(errno != EINPROGRESS) { cout << output << _FAIL << " (connect: " << strerror(errno) << ")\n"; return 0; } } if(con != 0) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(sock, &rset); FD_SET(sock, &wset); sel = select(sock + 1, &rset, &wset, NULL, &tv); if(sel == 0) { close(sock); cout << output << _FAIL << " (Timeout)\n"; return 0; } if(FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) { socklen_t len = sizeof(error); getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len); if(error) { cout << output << _FAIL << " (" << strerror(error) << ")\n"; return 0; } } else { cout << output << _FAIL << " (fd_set: " << strerror(errno) << ")\n"; return 0; } } fcntl(sock, F_SETFL, flags);
Das Problem liegt bei:
send(sock, sendbuf, sizeof(sendbuf), 0); // ... while(recv(sock, buf, _BUFLEN, 0)) { resp += buf; }
Da hängt es dann ewig, wenn der Server langsam ist.
-
Dann pruefe halt mit poll(2), ob Daten am Socket anliegen, bevor Du recv(2) aufrufst.
Uebrigens musst Du bei recv(2) auch den Fehlerstatus pruefen (errno abfragen, wenn -1 zurueckgegeben wird). recv(2) kann EINTR als Fehler zurueckliefern, wenn ein Signal an den Prozess zugestellt wurde.
-
Ich hab es jetzt so probiert:
struct pollfd fds; int pol; fds.fd = sock; fds.events = POLLIN; pol = poll(&fds, 1, _TIMEOUT); if(pol < 0) { cout << output << _FAIL << " (poll)\n"; return 0; } else if(pol == 0) { cout << output << _FAIL << " (Timeout)\n"; return 0; } else { if(fds.revents & POLLIN) { while(recv(sock, buf, _BUFLEN, 0)) { resp += buf; } } else { cout << output << _FAIL << " (pollevent)\n"; return 0; } }
Dabei kommt aber jedes mal ein Timeout. Hab ich da was falsch gemacht?
-
poll(2) wartet, bis entweder das Timeout verstrichen ist, oder das gewaehlte Ereignis aufgetreten ist.
Ausserdem kann noch EINTR auftreten.Der Timeout in diesem Fall ist ja kein Fehler, sondern heisst nur, dass nix passiert ist.
Wenn poll(2) -1 zurueckgibt, musst Du "errno" checken, ob EINTR gesetzt ist. In diesem Fall musst Du
poll(2) wiederholen. Dies kann vorkommen, wenn irgendwo im Programm ein Signal aufgetreten ist.Falls poll(2) dann immer noch nicht funktioniert, musst Du den Filedescriptor mittels fcntl(2)
auf O_ASYNC setzen (mit F_GETFL / F_SETFL). Dann bekommt der Prozess ein SIGIO Signal, wenn was passiert.Jedoch fallen alle blockierenden Systemaufrufe in allen Threads bei der Ankunft des SIGIO-Signals
mit einem Fehlercode EINTR raus, den Du dann abfangen und die Systemaufrufe wiederholen musst.UNIX Programme schreiben ist eine luschdige Angelegenheit.
-
Aber es bricht ja ab mit dem Rückgabewert 0, also Timeout. Und das jedes Mal.
Und eben bei Servern, bei denen ich genau weiß, dass sie nicht ans Timeout kommen (ohne poll funktionieren sie nämlich).Es bricht auch nicht nach den 10 Sekunden, die ich als Timeout eingestellt hab, ab, sondern schon nach weniger als einer Sekunde.
-
Zeig mal die Definition des _TIMEOUT Macros.
Da der Timeout bei poll() in Millisekunden gemessen wird, sollte da:
#define _TIMEOUT 10000
oder sowas stehen (fuer 10 Sek.)