Page 1 of 2 12 LastLast
Results 1 to 20 of 21

Thread: QTcpSocket Chat : Mutiple Client using FD_SET and select()

  1. #1
    Join Date
    Sep 2007
    Posts
    13
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I was able to send the message using a single client. Now I want to implement for multiple client. How can I do it under Qt using select() method. I prefer this because I have done it using normal socket without Qt.

    My application is using TCP socket which have client and server. I want to make a simple chat program which can 1 client send to server and server send to all the rest of client connected.

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    But is there a specific question here somewhere? And what does it have to do with Qt? I understand you are using BSD sockets, right?

  3. #3
    Join Date
    Sep 2007
    Posts
    13
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I using QTcpSocket. From the SimpleChat Example posted here, it is using QHash to store the client list. Is it possible to use select() method to do it?

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    If you use Qt sockets, don't use select(). It just won't work. Use signals and slots instead.

  5. The following user says thank you to wysota for this useful post:

    cooler123 (21st September 2007)

  6. #5
    Join Date
    Sep 2007
    Posts
    13
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    OIC. Thanks for the reply.

  7. #6
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Cool Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I wanted to follow up on thread in this year of 2012 and see if things might have changed, or if there is additional thoughts and wisdom based on
    changes that have been made to Qt since the original article posting.

    I have to manage up to 10 TCP-IP connections and singular UDP socket
    as the highest priority task in my application. I have used the Linux
    'select" API in the past successfully, and wanted to leverage this
    method for handling 10 Tcp Sockets in either separate threads, or
    perhaps a singular thread managing all of them.

    My current application implementation avoids using the default Qt
    event loop in secondary threads to more clearly control running
    thread activity (avoid duty cycle of event loop).

    The following code sample illustrates my first implementation using
    a QUdpSocket and a collection of QTcpSockets.

    It in general works, I can send and receive data with UDP recipients and
    TCP-IP servers.

    Now here is the downside. The Linux statement will exit in a few
    microseconds each time on the file descriptor corresponding to the
    TCP-IP socket. This will occur even when no data is available.
    As a result, my desire for this thread to pend until TCP-IP data
    becomes available does not happen, and this leads to great
    inefficiencies in the design.

    So the question I have is, is the QTcpSocket class fundamentally
    structured so that it can not be simple modified to handle this
    one issue? Is there a small tweak that can be made (say a
    subclass or socket option) that would be a quick fix to this
    singular issue.

    Assuming I replace the QTcpSocket class with standard Linux sockets,
    what value added features/performance of QTcpSocket would be badly missed
    (e.g.buffering).

    Wsoyata the sage, what do you think?

    Qt Code:
    1. timeval tvNow, tvTimeout, tvNextEpoch;
    2. tvNow = TimeUtils::getTime();
    3. tvTimeout = tvNow;
    4. tvNextEpoch = timevalNextEpoch(tvNow, m_epochMicroseconds);
    5.  
    6. quint32 processingDelayAdjustment = 0;
    7. fd_set readS, exS;
    8. int maxFd = 0;
    9.  
    10. #define SELECT_TIMING_TEST
    11. #ifdef SELECT_TIMING_TEST
    12. timeval tvStart, tvElapsed;
    13. tvStart = tvNow;
    14. tvElapsed = tvNow;
    15. #endif
    16.  
    17. // Task loop
    18. while (!m_stopped)
    19. {
    20. // Setup for ::select() statement
    21. FD_ZERO(&readS);
    22. FD_ZERO(&exS);
    23.  
    24. maxFd = 0;
    25. maxFd = qMax(maxFd, udpSocketDescriptor);
    26.  
    27. // Add UDP socket to read file descriptor set
    28. FD_SET(udpSocketDescriptor, &readS);
    29. FD_SET(udpSocketDescriptor, &exS);
    30.  
    31. // Iterate through list of active sockets TCP-IP sockets
    32. for (QMap<int, SockInfo *>::iterator it = m_currentSockets.begin(); it != m_currentSockets.end(); ++it)
    33. {
    34. int socket = it.key();
    35.  
    36. // Add TCP socket to read and exception file descriptor sets
    37. FD_SET(socket, &readS);
    38. FD_SET(socket, &exS);
    39.  
    40. maxFd = qMax(maxFd, socket);
    41. }
    42.  
    43. // Update select statement timeout value to the interval to the start of the next epoch
    44. timeoutUpdate(tvTimeout, tvNextEpoch, processingDelayAdjustment);
    45.  
    46. // Count of available file descriptors
    47. int count = 0;
    48.  
    49. // Invoke select if the timeout to the next epoch is non-zero
    50. if (tvTimeout.tv_usec != 0 || tvTimeout.tv_sec != 0)
    51. {
    52. #ifdef SELECT_TIMING_TEST
    53. #define SELECT_TIMING_TEST_LOGLEVEL logDEBUG4
    54. if (FILELog::ReportingLevel() >= SELECT_TIMING_TEST_LOGLEVEL)
    55. {
    56. // Get start time prior to select
    57. TimeUtils::getTime(&tvStart);
    58. msg = QString("Select In: now (%1/%2), timeout (%3/%4), tvNextEpoch (%5/%6)").arg(tvStart.tv_sec).arg(tvStart.tv_usec).arg(tvTimeout.tv_sec).arg(tvTimeout.tv_usec).arg(tvNextEpoch.tv_sec).arg(tvNextEpoch.tv_usec);
    59. FILE_LOG(SELECT_TIMING_TEST_LOGLEVEL) << qPrintable(msg);
    60. }
    61. #endif
    62.  
    63. // Wait for a number of file descriptors to change status or a timeout
    64. count = ::select(maxFd + 1, &readS, NULL, &exS, &tvTimeout);
    65.  
    66. #ifdef SELECT_TIMING_TEST
    67. if (FILELog::ReportingLevel() >= SELECT_TIMING_TEST_LOGLEVEL)
    68. {
    69. // Get system time after select call
    70. tvNow = TimeUtils::getTime();
    71.  
    72. // Display elapsed time and other content
    73. tvElapsed = tvNow - tvStart;
    74. msg = QString("Select Out: now (%1/%2), timeout (%3/%4), tvNextEpoch (%5/%6), elapsed (%7/%8), count %9").
    75. arg(tvNow.tv_sec).arg(tvNow.tv_usec).arg(tvTimeout.tv_sec).arg(tvTimeout.tv_usec).
    76. arg(tvNextEpoch.tv_sec).arg(tvNextEpoch.tv_usec).arg(tvElapsed.tv_sec).arg(tvElapsed.tv_usec).arg(count);
    77. FILE_LOG(SELECT_TIMING_TEST_LOGLEVEL) << qPrintable(msg);
    78. }
    79. #endif
    80. }
    81.  
    82. // Get system time after select call
    83. tvNow = TimeUtils::getTime();
    84.  
    85. // On epoch boundary, perform epoch activities
    86. if (tvNextEpoch <= tvNow)
    87. {
    88. // Update the time of the next epoch (calculate the time of day to the start of the next epoch boundary)
    89. tvNextEpoch = timevalNextEpoch(tvNow, m_epochMicroseconds);
    90.  
    91. // Should be aligned to even multiple of epoch on timeout. Calculate how far off it was to make runtime adjustment for the next pass (routine timeoutUpdate())
    92. processingDelayAdjustment = tvNow.tv_usec % m_epochMicroseconds;
    93.  
    94. if (FILELog::ReportingLevel() >= logDEBUG4)
    95. {
    96. int epochNumber = timevalToEpoch(tvNow.tv_usec, m_epochMicroseconds);
    97.  
    98. msg = QString("TDMA: Epoch %1 at (%2/%3), timeout (%4/%5), delay adjust (%6)").
    99. arg(epochNumber).arg(tvNow.tv_sec).arg(tvNow.tv_usec).arg(tvTimeout.tv_sec).arg(tvTimeout.tv_usec).arg(processingDelayAdjustment);
    100. FILE_LOG(logDEBUG4) << qPrintable(msg);
    101. }
    102.  
    103. onEpoch();
    104.  
    105. } // end Epoch activities
    106.  
    107.  
    108.  
    109. // Exit thread when stopped
    110. if (m_stopped)
    111. {
    112. break;
    113. }
    114.  
    115. // Packets found, process them
    116. if (count > 0)
    117. {
    118.  
    119. // RadioG-LM TA radio queue status messages
    120. if (FD_ISSET(udpSocketDescriptor, &readS))
    121. {
    122. if (FILELog::ReportingLevel() >= logDEBUG4)
    123. {
    124. msg = QString("TDMA: select() exit with count %1: UDP socket read file descriptor %2 set at (%3/%4)").
    125. arg(count).arg(udpSocketDescriptor).arg(tvNow.tv_sec).arg(tvNow.tv_usec);
    126. FILE_LOG(logDEBUG4) << qPrintable(msg);
    127. }
    128.  
    129. static QHostAddress recvFrom;
    130. static QByteArray datagram;
    131.  
    132. // Assemble a full UDP datagram
    133. do
    134. {
    135. datagram.resize(m_udpSocket->pendingDatagramSize());
    136. m_udpSocket->readDatagram(datagram.data(), datagram.size(), &recvFrom);
    137. } while (m_udpSocket->hasPendingDatagrams());
    138.  
    139. // Process contents of UDP datagram
    140. processLinkManagerMessage(datagram, recvFrom);
    141. }
    142.  
    143. // UDP socket exception
    144. if (FD_ISSET(udpSocketDescriptor, &exS))
    145. {
    146. FILE_LOG(logERROR) << "TDMA: select() exception detected for UDP socket: " << udpSocketDescriptor;
    147. }
    148.  
    149. // Check all TCP-IP LM-QM connections in the collection
    150. for (QMap<int, SockInfo *>::iterator it = m_currentSockets.begin(); it != m_currentSockets.end(); ++it)
    151. {
    152. int socket = it.key();
    153.  
    154. // Socket read
    155. if (FD_ISSET(socket, &readS))
    156. {
    157. if (FILELog::ReportingLevel() >= logDEBUG4)
    158. {
    159. msg = QString("TDMA: select() exit with count %1: TCP-IP socket read file descriptor %2 set at (%3/%4)").
    160. arg(count).arg(socket).arg(tvNow.tv_sec).arg(tvNow.tv_usec);
    161. FILE_LOG(logDEBUG4) << qPrintable(msg);
    162. }
    163.  
    164. // QM TCP-IP receive handler
    165. SockInfo *sockInfo = it.value();
    166. if (sockInfo)
    167. {
    168. processSocketMessage(sockInfo);
    169. }
    170. }
    171.  
    172. }
    173.  
    174. } // end count > 1
    175.  
    176. } // end while
    To copy to clipboard, switch view to plain text mode 

  8. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Qt uses standard sockets too, so if you're just after calling select manually, you can do that if you really want to, however I don't see how your code would be faster than what Qt does (apart the trick Qt does with adding an extra descriptor to the set).

    As for your problem... try detecting WHAT causes select() to return (EINTR maybe?).
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  9. #8
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Hi Wyosota, thanks for the reply (and to all the past suggestions). The motivation for using the select() method is in part to reduce the thread count to manage 10 active TCP-IP client connections.

    I also need to transmit along a fairly constrained time line of say a 100 ms epoch which begins with a burst of UDP traffic, then TCP-IP traffic, following by schedule calculation.

    Using the singular select() implementation as the highest priority thread works out well for this requirement. I do not want to have potentially 10 TCP-IP threads as blocking clients to manage and the additional locking
    on handling shared data between these threads and the UDP thread.

    To answer your observation on why select() exits, I was unable to display all of the souce code because of message limitations, but select is exiting with a count of 1, and the file descriptor set upon exit corresponds to
    the file descriptor for the singular TCP-IP socket that is opened. I can observe this under Windows and Linux by inspecting the fd_set file descriptor for read.

    When the TCP-IP socket is NOT opened, then select exits according to a 100ms epoch rate that is being calculated. This results in the duty cycle that I desire for this particular thread, which is
    intended to suspend as much as possible after completing its activities.

    Walking into my application code when select exits, FD_ISSET is true for the singular TCP-IP socket that I am using for testing (this will expand to 10 TCP-IP connections).

    When I get to the routines to process TCP-IP messages received, the call to socket->bytesAvailable() is most frequently zero. When I do finally get data, the frames are correctly decoded.

    So the symptoms are essentially that select() is exiting on the TCP-IP socket unnecessarily on the read file descriptor for the socket. If this did not occur, it would be perfect solution for
    my requirements.

    Looking through some of the Qt source code for QTcpSocket, is it based on buffered content, and this is a requirement for the class.

    How the Qt implementation some how provides a file descriptor whose attributes cause select() to exit prematurely from my perspective is completely unknown to me. I am stumped on this one, and have
    no idea of how to troubleshoot select() further. Note that this behavior is roughly consistent under both Windows and Linux.

    There are no select() error conditions upon exit from this API.

    While I desire to have the code cross platform, the deployed platform will be Linux exclusively. Still, the Qt framework for sockets provided the platform abstraction on sockets that I desired.

    I am certainly willing to code the socket routines using simple BSD sockets, but wanted to do some due diligence before I toss the QTcpSocket implementation in case there was something simple that I
    might have missed. I have not viewed any particular socket option that I think would change the behavior.

    Assuming I have to go outside Qt to get to the more primitive socket APIs, is there any C++ wrapper available that provides both BSD and Winsock capability for cross platform support that you would
    recommend?

    I am certainly open to other architectural options, perhaps a separate thread (or threads) based on the event loop and signals/slots.

    However, performance of this single networking thread is the essence of my application, which will either succeed or fail based on the implementation. I have to perform activities at a relatively
    frequent interval (as quick as possible 25 ms epochs), so speed and no delays are important attributes. That is what made use of select() attractive, and perhaps one could argue that the
    more primitive socket APIs might also provide performance benefits.

    In a nutshell, the file descriptor returned from QTcpSocket seems to trigger as read active even when this is not the case. I don't know why.


    Added after 8 minutes:


    Missing source code for select() error handling (based on message size limitations)

    Qt Code:
    1. } // end count > 1
    2.  
    3. else
    4. {
    5. switch (count)
    6. {
    7. // Timeout
    8. case 0:
    9. break;
    10.  
    11. // Error
    12. case -1:
    13.  
    14. // Ignore a non blocked signal being caught
    15. #ifdef Q_OS_WIN
    16. if (errno != WSAEINTR)
    17. {
    18. FILE_LOG(logERROR) << "TDMA: select() statement error exit: " << WSAGetLastError();
    19. ++m_nSelectErrors;
    20.  
    21. // An invalid file descriptor was given
    22. if (errno == WSAEBADF)
    23. {
    24. clearClosedSocketsFromCollection();
    25. }
    26. }
    27. #else
    28. if (errno != EINTR)
    29. {
    30. // Linux: Select returns -1 on error and sets errno appropriately
    31. SysUtils::handleLastError("TDMA: select() statement error exit");
    32. ++m_nSelectErrors;
    33.  
    34. // An invalid file descriptor was given
    35. if (errno == EBADF)
    36. {
    37. clearClosedSocketsFromCollection();
    38. }
    39. }
    40. #endif
    41. break;
    42.  
    43. // Unknown select return value
    44. default:
    45. ++m_nSelectErrors;
    46. FILE_LOG(logERROR) << "TDMA: select returned " << count;
    47. break;
    48. }
    49.  
    50. } // end else if (count > 0)
    51.  
    52. //debugElapsed();
    53.  
    54. } // end while
    To copy to clipboard, switch view to plain text mode 
    Last edited by bob2oneil; 10th January 2012 at 05:52.

  10. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Quote Originally Posted by bob2oneil View Post
    The motivation for using the select() method is in part to reduce the thread count to manage 10 active TCP-IP client connections.
    Qt can do that with exactly 0 additional threads. I doubt you can do better with a manual select().

    To answer your observation on why select() exits, I was unable to display all of the souce code because of message limitations, but select is exiting with a count of 1, and the file descriptor set upon exit corresponds to
    the file descriptor for the singular TCP-IP socket that is opened. I can observe this under Windows and Linux by inspecting the fd_set file descriptor for read.
    Is there anything to read from the descriptor after the such situation occurs?

    When I get to the routines to process TCP-IP messages received, the call to socket->bytesAvailable() is most frequently zero.
    How did you implement bytesAvailable() for your socket? As I understand you are not using QTcpSocket... If you are using QTcpSocket without letting Qt process the socket, you can't rely on the infrastructure the class provides. bytesAvailable() will likely return incorrect values. I think in your situation there is really no point in using QTcpSocket if you want to do all the processing yourself anyway. What benefits does it bring?

    By the way, your code logic is really strange... Especially the fragment when you discard all UDP datagrams but the last one. You are aware that the last datagram you receive doesn't have to be the last that is sent as UDP can reorder datagrams?
    Last edited by wysota; 10th January 2012 at 12:17.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  11. The following user says thank you to wysota for this useful post:

    bob2oneil (13th January 2012)

  12. #10
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Wink Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Is there anything to read from the descriptor after the such situation occurs?
    No, QTcpSocket->bytesAvailable() returns 0. It is as if the socket file descriptor always indicates that read is available, but it is not. When I do send a message to this socket, it is received correctly.


    How did you implement bytesAvailable() for your socket? As I understand you are not using QTcpSocket... If you are using QTcpSocket without letting Qt process the socket, you can't rely on the infrastructure the class provides. bytesAvailable() will likely return incorrect values. I think in your situation there is really no point in using QTcpSocket if you want to do all the processing yourself anyway. What benefits does it bring?
    It was not obvious from my original source code (due to size limits), but sockInfo* contains a QTcpSocket as an element from the following code sample for the socket connection:

    Qt Code:
    1. const int Timeout = 5 * 1000;
    2.  
    3. // Create socket
    4. QTcpSocket *socket = new QTcpSocket();
    5.  
    6. // Attempt LM-QM connection
    7. QString serverIP = m_queueManagerCommSettings.at(i)->getIP();
    8. quint16 serverPort = m_queueManagerCommSettings.at(i)->getPort();
    9. socket->connectToHost(QHostAddress(serverIP), serverPort);
    10.  
    11. // Wait on connection or failure
    12. if (!socket->waitForConnected(Timeout))
    13. {
    14. FILE_LOG(logWARNING) << "TDMA: failed making LM to QM TCP-IP socket connection at ip '" << qPrintable(serverIP) << "' - port " << serverPort << ", error: " << qPrintable(socket->errorString());
    15. delete socket;
    16. ++m_nQMConnectErrors;
    17. }
    18. else
    19. {
    20. int socketDescriptor = socket->socketDescriptor();
    21.  
    22. FILE_LOG(logINFO) << "TDMA: TCP-IP socket (descriptor " << socketDescriptor << ") connection made from LM to QM at ip '" << qPrintable(serverIP) << "' - port " << serverPort;
    23. ++m_nQMConnects;
    24.  
    25. // Add connected socket to collection
    26. m_currentSockets.insert(socketDescriptor, new SockInfo(socket, m_queueManagerCommSettings.at(i)->getTransceiverID(), 0));
    27.  
    28. // Optimize socket for latency, set to TCP_NODELAY to diable Nagle's algorithm, set to 1 to enable
    29. const QVariant value = 0;
    30. socket->setSocketOption(QAbstractSocket::LowDelayOption, value);
    31.  
    32. // Set DSCP field of socket to high priority setting
    33. if (!SysUtils::setSocketTypeOfService(socket->socketDescriptor(), dscpNetworkCritical))
    34. {
    35. ++m_nErrors;
    36. FILE_LOG(logERROR) << "TDMA: failed to set TCP-IP transmit socket DSCP value with errno - " << qPrintable(SysUtils::getErrorString());
    37. }
    38. }
    To copy to clipboard, switch view to plain text mode 


    As far as my UDP processing looking strange, I am not sure I understand. The build loop is just like the content from the Thelin text that you had a hand in, I simply have a do--while rather than a while .. where I assume
    since the UDP file read descriptor indicated reception of data, then there was content to read. I can certainly change this. Maybe I am missing your point.

  13. #11
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Quote Originally Posted by bob2oneil View Post
    No, QTcpSocket->bytesAvailable() returns 0. It is as if the socket file descriptor always indicates that read is available, but it is not. When I do send a message to this socket, it is received correctly.
    I'm not asking what bytesAvailable() returns, I'm asking whether there is actual data to be read. If you're not letting Qt process the socket, I would assume bytesAvailable() returns bogus data. Get the socket descriptor and call ::read() on it.


    As far as my UDP processing looking strange, I am not sure I understand. The build loop is just like the content from the Thelin text that you had a hand in, I simply have a do--while rather than a while
    I don't think so...

    Here is your code:
    Qt Code:
    1. static QHostAddress recvFrom;
    2. static QByteArray datagram;
    3.  
    4. // Assemble a full UDP datagram
    5. do
    6. {
    7. datagram.resize(m_udpSocket->pendingDatagramSize());
    8. m_udpSocket->readDatagram(datagram.data(), datagram.size(), &recvFrom);
    9. } while (m_udpSocket->hasPendingDatagrams());
    10.  
    11. // Process contents of UDP datagram
    12. processLinkManagerMessage(datagram, recvFrom);
    To copy to clipboard, switch view to plain text mode 

    You exit the loop when the socket has no more pending datagrams and while you are in the loop you are only reading datagrams from the socket overwriting what was written in the previous iteration. Only when you exit the loop you do some kind of processing. So effectively you are processing the last datagram only and discarding all the previous ones. You are trying to process something even if there are no datagrams available at all. I would assume the loop should look like the following:

    Qt Code:
    1. static QHostAddress recvFrom;
    2. static QByteArray datagram;
    3.  
    4. do {
    5. datagram.resize(m_udpSocket->pendingDatagramSize());
    6. m_udpSocket->readDatagram(datagram.data(), datagram.size(), &recvFrom);
    7. // Process contents of UDP datagram
    8. processLinkManagerMessage(datagram, recvFrom);
    9. } while (m_udpSocket->hasPendingDatagrams());
    To copy to clipboard, switch view to plain text mode 

    I don't have Johan's book at hand right now so I can't verify what you say but I'd assume the book tells you to process the datagram while being inside the loop.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  14. The following user says thank you to wysota for this useful post:

    bob2oneil (13th January 2012)

  15. #12
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Cool Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I'm not asking what bytesAvailable() returns, I'm asking whether there is actual data to be read. If you're not letting Qt process the socket, I would assume bytesAvailable() returns bogus data. Get the socket descriptor and call ::read() on it.
    Hi Wytold, good suggestion, I was not sure where you were heading with this one. While I am not executing an event loop or using signals/slots for QTcpSocket, I am using the blocking modes APIs similar to the blocking samples. Nonetheless, when I call ::recv() on the socket file descriptor, this API is able to read the raw socket data, and by reading the data, the file descriptor no longer passes immediately through the select() statement. It would seem that the QTcpSocket.read API, which reads buffered content, does not perform the native socket read analogous to ::recv. The call stack for the QTcpSocket read is QIODevice::read() to QAbstractSocket::readData. Perhaps this proves your original statement that QTcpSocket does not work well with select(). I believe I have a solution using it, but requires bypassing the buffering to get to the raw socket read API. However, having done this, the logical next step is to perhaps use the raw socket APIs directly in the interest of speed and performance. Nonetheless, excellent observation that explains a bit what the observed behavior is.

    On the topic of my UDP code, you are correct. Sometimes stuff is right in front of your eyes, and it takes a 2nd pair of eyes to see the obvious.

    Thanks again.

  16. #13
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I see no benefit in using QTcpSocket for your tcp sockets. Since you are using select(), you are limiting yourself to one platform only, so you might as well use ::socket().
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  17. The following user says thank you to wysota for this useful post:

    bob2oneil (13th January 2012)

  18. #14
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Cool Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    My code is actually still cross platform (select/recv etc are all available under Windows/Winsock)

    Where the benefit of using QTcpSocket to establish the connections, is that I will eventually have to use secure sockets for these comm interfaces, and QSslSocket seems like a real good match that will reduce my coding efforts.
    The devil well be in the details on this effort.

    By using the low level socket APIs I am to some extent shortcutting the buffering required in the base class, and I am using TCP-IP to send small (32 byte) control frames that need to be handled at epoch rates of 100 ms and less.

  19. #15
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    TCP isn't really a good competitor when it comes to time critical tasks since a temporary network congestion disrupts further communication.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  20. The following user says thank you to wysota for this useful post:

    bob2oneil (13th January 2012)

  21. #16
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Wink Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    No argument, this protocol was imposed on me, it was not my selection. I am trying to make the most of it, open to ideas. One step is to get closer to the socket APIs, and to set selected socket properties.
    The frames are very small but frequent (up to 10 send and receive in 100 ms epochs).

  22. #17
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Small frames (or actually segments) is another thing TCP doesn't like. If you want to send those really quick, you have to force a push flag on each segment otherwise TCP will be trying to wait for more data.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  23. The following user says thank you to wysota for this useful post:

    bob2oneil (14th January 2012)

  24. #18
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    I have the DSCP field set to high prioirty and have the TCP_NODELAY option on the socket as well. I am not sure what else I can do. It is important that these small frames are sent without delay (hence the possible need to avoid the QTcpSocket buffering) on both send and receive. I also have UDP traffic being sent with similar timing requirements. If there is a viable chance that the small TCP-IP sends can delay the timely delivery of the more important UDP traffic, I can certainly put all the TCP-IP routine in its own thread. However, the single high prioirty thread with both does offer some advantages, especially when I want to define time line sequences where a UDP burst (20 small frames) is quickly followed by a TCP-IP burst (10 frames) at a 100 ms epoch rate.

    Next wrinkle is using SSL/OpenSLL on both protocols/sockets efficiently.

    Thanks for the suggestions.


    Added after 19 minutes:


    Quick read on the PSH flag you mentioned in the TCP-IP header control flags, not sure if these map to the socket option (TCP_NODELAY) or how they may be set programmatically.

    Flags (9 bits) (aka Control bits) – contains 9 1-bit flags

    PSH (1 bit) – Push function. Asks to push the buffered data to the receiving application.
    Last edited by bob2oneil; 14th January 2012 at 18:19.

  25. #19
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    If I remember correctly at least the TCP stack implementation on Linux sets the push flag when you flush the socket.

    Anyway, I don't think it matters how many threads you use and if you use any additional threads at all unless you are trying to handle hundreds of sockets at the same time. You have no influence on what happens over the link which is the real bottleneck in networking problems. And once the traffic increases, congestion will start to happen, you'll get retransmissions and you'll have to say good bye to your time critical design. The more small segments you send, the bigger the chance for congestion as you'll be wasting frames in the link layer (e.g. in Ethernet). And the faster the network, the larger the frames so you lose more and more making yourself more vulnerable to congestion and retransmissions. This design is simply flawed and you should talk to the person who imposed the protocol on you to seriously reconsider. Unless of course you are doing it all in LAN or even in a point-to-point connection. But then you should be using UDP anyway.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  26. The following user says thank you to wysota for this useful post:

    bob2oneil (15th January 2012)

  27. #20
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Smile Re: QTcpSocket Chat : Mutiple Client using FD_SET and select()

    Hi Wytold, I will do the socket flush and check Wireshark to see if the push flag bit is being set. Thanks for the input on the protocol, I really can't push back on that decision, we tried months ago suggesting UDP was a better fit, but you know how external decisions can be imposed on a design. I will be cognizant of the loading and retry issues in our testing, with perhaps a "I told you so" prepared to be launched somewhere down the line. I guess if I can find ways of making TCP-IP behave more like UDP, all the better. I really don't want any transmit retries, as the information returned will be stale at the time of the new request, which will be the same request message. There is some notion of the recipient sending out data unsolicited at a perioidic 100 ms epoch rate once a connection is established (the other end is the TCP-IP server), but then the retry issue occurs on their end.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.