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:
		@@ -137,7 +137,12 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
 | 
			
		||||
    so_linger.l_onoff = 1;
 | 
			
		||||
    so_linger.l_linger = 0;
 | 
			
		||||
 | 
			
		||||
    int maxfdp1;
 | 
			
		||||
    fd_set rset;
 | 
			
		||||
    fd_set wset;
 | 
			
		||||
 | 
			
		||||
    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_RCVTIMEO, &tv, sizeof(tv));
 | 
			
		||||
    int flag = 1;
 | 
			
		||||
@@ -151,24 +156,56 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
 | 
			
		||||
    int loop = 0;
 | 
			
		||||
    int bytesWritten = 0;
 | 
			
		||||
    while (bytesWritten < bytesToWrite) {
 | 
			
		||||
        int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0);
 | 
			
		||||
        if (n >= 0) {
 | 
			
		||||
            bytesWritten += n;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (errno == EWOULDBLOCK) {
 | 
			
		||||
                if (++loop < 10) {
 | 
			
		||||
                    QThread::msleep(500);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                    QString("WRITE TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
 | 
			
		||||
                ::close(sockfd);
 | 
			
		||||
                return std::nullopt;
 | 
			
		||||
            } else
 | 
			
		||||
        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("WRITE INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")");
 | 
			
		||||
                    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);
 | 
			
		||||
            if (n >= 0) {
 | 
			
		||||
                bytesWritten += n;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (errno == EWOULDBLOCK) {
 | 
			
		||||
                    if (++loop < 10) {
 | 
			
		||||
                        QThread::msleep(500);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                        QString("WRITE TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
 | 
			
		||||
                    ::close(sockfd);
 | 
			
		||||
                    return std::nullopt;
 | 
			
		||||
                } else
 | 
			
		||||
                if (errno == EINTR) {
 | 
			
		||||
                    printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                        QString("WRITE INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")");
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -188,33 +225,66 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) {
 | 
			
		||||
    int bytesRead = 0;
 | 
			
		||||
    while (bytesRead < bytesToRead) {
 | 
			
		||||
        errno = 0;
 | 
			
		||||
        int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead,
 | 
			
		||||
                           0, NULL, NULL);
 | 
			
		||||
        if (n > 0) { //
 | 
			
		||||
            bytesRead += n;
 | 
			
		||||
        } else
 | 
			
		||||
        if (n == 0) {
 | 
			
		||||
            // The return value will be 0 when the peer has performed an orderly shutdown.
 | 
			
		||||
            printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                QString("PEER CLOSED CONNECTION (") + strerror(errno) + ")");
 | 
			
		||||
            ::close(sockfd);
 | 
			
		||||
            return std::nullopt;
 | 
			
		||||
        } else
 | 
			
		||||
        if (n < 0) {
 | 
			
		||||
            if (errno == EWOULDBLOCK) {
 | 
			
		||||
                if (++loop < 10) {
 | 
			
		||||
                    QThread::msleep(500);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                        QString("READ TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
 | 
			
		||||
                ::close(sockfd);
 | 
			
		||||
                return std::nullopt;
 | 
			
		||||
            }
 | 
			
		||||
        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,
 | 
			
		||||
                                   0, NULL, NULL);
 | 
			
		||||
                if (n > 0) { //
 | 
			
		||||
                    bytesRead += n;
 | 
			
		||||
                } else
 | 
			
		||||
                if (n == 0) {
 | 
			
		||||
                    // The return value will be 0 when the peer has performed an orderly shutdown.
 | 
			
		||||
                    printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                        QString("PEER CLOSED CONNECTION (") + strerror(errno) + ")");
 | 
			
		||||
                    ::close(sockfd);
 | 
			
		||||
                    return std::nullopt;
 | 
			
		||||
                } else
 | 
			
		||||
                if (n < 0) {
 | 
			
		||||
                    if (errno == EWOULDBLOCK) { // check just in case
 | 
			
		||||
                        if (++loop < 10) {
 | 
			
		||||
                            QThread::msleep(500);
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                                QString("READ TIMEOUT %1(").arg(loop) + strerror(errno) + ")");
 | 
			
		||||
                        ::close(sockfd);
 | 
			
		||||
                        return std::nullopt;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (errno == EINTR) {
 | 
			
		||||
                        printErrorMessage(port, clientIP, clientPort,
 | 
			
		||||
                            QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")");
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user