使用单线程同步或异步连接多个客户端?

时间:2017-03-09 18:18:15

标签: c++ qt sockets asynchronous

几个月前,我曾向我的项目询问过有关qt tcp网络编程的一些相关问题。最后软件出来了。但在了解了有关qt event mechanssignals -slots实现的更多细节之后。我担心程序性能和稳定性。

关键问题是服务器使用单个线程来处理多个tcpsockets。实现简介代码:

  1. incomingConnection函数编写如下:两个QSignalMapper用于映射每个套接字SlotReadyReadSlotDisconnected
  2. void ServerModule::incomingConnection(qintptr socketDescriptor)
     {
        m_tcpSocket = new QTcpSocket(this);
        if (!m_tcpSocket->setSocketDescriptor(socketDescriptor)) {
            return;
        }
        connect(m_tcpSocket, SIGNAL(readyRead()), m_readyReadSignalMapper, SLOT(map()));
         m_readyReadSignalMapper->setMapping(m_tcpSocket, m_tcpSocket);
         connect(m_tcpSocket, SIGNAL(disconnected()), m_disconnectedSignalMapper, SLOT(map()));
         m_disconnectedSignalMapper->setMapping(m_tcpSocket, m_tcpSocket);
         }
    
    1. 服务器构造函数:
    2. ServerModule::ServerModule(QObject * parent) : QTcpServer(parent),m_readyReadSignalMapper(new QSignalMapper(this)),m_disconnectedSignalMapper(new QSignalMapper(this))
      {
      
          connect(m_readyReadSignalMapper, SIGNAL(mapped(QObject *)), this, SLOT(SlotReadyRead(QObject *)));
          connect(m_disconnectedSignalMapper, SIGNAL(mapped(QObject *)), this, SLOT(SlotDisconnected(QObject*)));
             ......other code 
      }
      
      1. SlotReadyReadSlotDisconnected函数都是正常的,它会将QObject* socketObject转换为QTcpSocket *socket,然后再做一些工作。
      2. 所以我已经了解到,如果写一下connect函数与特定的connect mode

          

        Qt的:: DirectConnection
          Qt的:: QueuedConnection
          Qt的:: BlockingQueuedConnection

        如果使用单线程,则只能使用第一模式和第二模式。并使用第一种模式,它是 同步 ,如果是第二种,它是 异步 。我的程序默认使用第一种模式,因此signals -slots已连接 同步

        所以这是我的担心: 1.处理多个套接字的单线程性能如何?它能达到每秒500个连接的并发性吗?可以处理的最大连接数是多少? 2.如果同时有多个套接字准备好读取,然后调用qt core来发送readyRead信号,QSignalMapper可以正确处理它吗? 3.我应该使用 QueuedConnection 模式连接socketQSignalMapper,因为此连接未立即连接,而是使用event queuepostEvent要联系。所以它的 异步 。在这种情况下,可以处理并发请求吗?

1 个答案:

答案 0 :(得分:1)

首先,您的代码存在一些问题:

  1. QSignalMapper只会增加代码的复杂性
  2. 不要为QTcpSocket使用成员变量,每次都会覆盖它,很可能会使用无效指针
  3. 如果您没有做复杂的事情或使用QSslSocket不要将QTcpServer子类化,只需连接到newConnection()信号并调用nextPendingConnection()
  4. 现在,在您提问的连接部分,您应该简要阅读https://woboq.com/blog/how-qt-signals-slots-work.html

    • Qt :: DirectConnection - 表示一旦发出信号,插槽将立即被调用,如果具有该插槽的对象存在于另一个线程中,则它将在调用者线程中被调用。如果使用Qt :: AutoConnection并且两个对象都存在于同一个线程中,则使用此方法。
    • Qt :: QueuedConnection - 意味着当发出信号时,它会创建一个事件,并在返回到事件循环后被置于事件循环上,这对于不同的线程很有用,因为插槽将被调用对象所在的线程。这不是你想要的,也不应该使用QTcpSocket(阅读文档)。
    • Qt :: BlockingQueuedConnection - 很少使用这个,这就像上面的事件将事件放在另一个线程的事件循环上,但它会阻塞直到调用插槽。

    唯一的"同步"方法是阻塞的,因为它会阻塞,但这并不意味着它是一个问题,你可以有一个非gui线程阻止等待gui线程的回复,同时要求用户密码,例如。

    现在的问题:

    更新1和3以获得更多说明

    1. 性能实际上取决于这些套接字将执行的操作,处理请求和发送回复所需的时间,在Cutelyst Web Framework i5上的单个线程可以处理高达100K req / s(返回' Hello Wolrd!'字符串HTTP响应),并发连接的最大数量将受可用的最大端口限制,可用于分配内存的内存,当然,如果CPU密集,客户端将等待很长时间。
    2. 最常见的方法是在您的广告位上调用sender(),因为小点击的性能(发送者()发出呼叫),使用lambda会更快:

      void Parser::readyRead() {
           auto socket = qobject_cast<QTcpSocket*>(sender());
           ...
      }
      

      或者使用lambda:

      connect(sock, &QTcpSocket::readyRead, [sock] () {
          // use sock obj here
      });
      
      1. 再次QSignalMapper不是你想要的,你应该调用sender()来获取QTcpSocket或使用lambda捕获套接字对象(这样会快一点)
      2. 不应该在QSignalMapper上使用QueuedConnection连接,因为这会导致不必要的开销,因为它会将事件放在事件循环队列中并且稍后会被处理,这也可能导致奇怪的错误由于QTcpSockets处理数据的方式(你必须消耗readyReady上的所有bytesAvailable(),因为如果没有额外的数据到达readyRead()信号不会再次发出。