SO_SNDTIMEO and SO_RCVTIMEO socket options have turned out not to be reliable.

Use select() for detecting timeout on socket (read and write).
This commit is contained in:
Gerhard Hoffmann 2023-08-14 14:35:54 +02:00
parent 66d0214720
commit 8d528f0f55

View File

@ -137,7 +137,12 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
so_linger.l_onoff = 1; so_linger.l_onoff = 1;
so_linger.l_linger = 0; so_linger.l_linger = 0;
int maxfdp1;
fd_set rset;
fd_set wset;
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
// no reliable, but does not harm, as we use select() as well
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
int flag = 1; int flag = 1;
@ -151,6 +156,37 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
int loop = 0; int loop = 0;
int bytesWritten = 0; int bytesWritten = 0;
while (bytesWritten < bytesToWrite) { while (bytesWritten < bytesToWrite) {
errno = 0;
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
maxfdp1 = sockfd + 1;
tv.tv_sec = 60; /* 60 secs timeout for read and write -> APISM cuts the connection after 30s */
tv.tv_usec = 0;
int const w = select(maxfdp1, NULL, &wset, NULL, &tv);
if (w < 0) { // error
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")");
continue;
} else {
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-ERROR (WRITE) %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
}
} else
if (w == 0) { // timeout
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-TIMEOUT (WRITE) %1(").arg(loop) + strerror(errno) + ")");
if (++loop < 10) {
QThread::msleep(500);
continue;
}
::close(sockfd);
return std::nullopt;
} else
if (w > 0) {
int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0); int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0);
if (n >= 0) { if (n >= 0) {
bytesWritten += n; bytesWritten += n;
@ -172,6 +208,7 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
} }
} }
} }
}
// DO NOT USE SHUTDOWN! APISM CAN NOT COPE WITH IT // DO NOT USE SHUTDOWN! APISM CAN NOT COPE WITH IT
// errno = 0; // errno = 0;
@ -188,6 +225,37 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
int bytesRead = 0; int bytesRead = 0;
while (bytesRead < bytesToRead) { while (bytesRead < bytesToRead) {
errno = 0; errno = 0;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
maxfdp1 = sockfd + 1;
tv.tv_sec = 60; /* 60 secs timeout for read and write */
tv.tv_usec = 0;
int const r = select(maxfdp1, &rset, NULL, NULL, &tv);
if (r < 0) { // error
if (errno == EINTR) {
printErrorMessage(port, clientIP, clientPort,
QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")");
continue;
} else {
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-ERROR (READ) %1(").arg(loop) + strerror(errno) + ")");
::close(sockfd);
return std::nullopt;
}
} else
if (r == 0) { // timeout
printErrorMessage(port, clientIP, clientPort,
QString("SELECT-TIMEOUT (READ) %1(").arg(loop) + strerror(errno) + ")");
if (++loop < 10) {
QThread::msleep(500);
continue;
}
::close(sockfd);
return std::nullopt;
} else
if (r > 0) {
if (FD_ISSET(sockfd, &rset)) {
int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead, int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead,
0, NULL, NULL); 0, NULL, NULL);
if (n > 0) { // if (n > 0) { //
@ -201,7 +269,7 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
return std::nullopt; return std::nullopt;
} else } else
if (n < 0) { if (n < 0) {
if (errno == EWOULDBLOCK) { if (errno == EWOULDBLOCK) { // check just in case
if (++loop < 10) { if (++loop < 10) {
QThread::msleep(500); QThread::msleep(500);
continue; continue;
@ -217,6 +285,8 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
continue; continue;
} }
} }
}
}
printInfoMessage(port, clientIP, clientPort, QString("MESSAGE RECEIVED ") + buf); printInfoMessage(port, clientIP, clientPort, QString("MESSAGE RECEIVED ") + buf);
QString response(buf); QString response(buf);