gpt4 book ai didi

c++ - 具有大量并发客户端的慢速 QTcpServer

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:41:00 31 4
gpt4 key购买 nike

我正在 Qt 中编写 TCP 服务器,它将提供大文件。应用逻辑如下:

  1. 我继承了 QTcpServer 并重新实现了 incomingConnection(int)
  2. 在 incomingConnection 中,我正在创建“Streamer”类的实例
  3. “Streamer”正在使用 QTcpSocket,它使用来自 incomingConnection 的 setSocketDescriptor 初始化
  4. 当来自客户端的数据到达时,我从 readyRead() 槽中发回初始响应,然后我将套接字的信号 bytesWritten(qint64) 连接到 Streamer 的槽 bytesWritten()

bytesWritten 看起来像这样:

Streamer.h:
...
private:
QFile *m_file;
char m_readBuffer[64 * 1024];
QTcpSocket *m_socket;
...

Streamer.cpp
...
void Streamer::bytesWritten() {
if (m_socket->bytesToWrite() <= 0) {
const int bytesRead = m_file->read(m_readBuffer, 64 * 1024);
m_socket->write(m_readBuffer, bytesRead);
}
}
...

所以基本上我只在所有待处理数据都已完全写入时才写入新数据。我认为这是最异步的方式。

一切正常,除了当有很多并发客户端时速度很慢。

大约有 5 个客户端 - 我从该服务器下载的速度约为 1 MB/s(我家庭互联网连接的最大值)

大约有 140 个客户端 - 下载速度约为 100-200 KB/s。

服务器的互联网连接速度为 10 Gbps,140 个客户端的使用速度约为 100 Mbps,所以我认为这不是问题所在。

140 个客户端的服务器内存使用量 - 100 MB 可用 2GB

服务器的 CPU 使用率 - 最高 20%

我正在使用端口 800。

当端口 800 上有 140 个客户端并且通过它的下载速度大约为 100-200 KB/s 时,我在端口 801 上运行单独的拷贝并且以 1MB/s 的速度下载没有问题。

我的猜测是,Qt 的事件调度(或套接字通知程序?)以某种方式太慢而无法处理所有这些事件。

我试过:

  1. 使用 -O3 编译整个 Qt 和我的应用
  2. 安装libglib2.0-dev并重新编译Qt(因为QCoreApplication使用了QEventDispatcherGlib或者QEventDispatcherUNIX,所以想看看有没有区别)
  3. 生成一些线程并在 incomingConnection(int) 中使用 streamer->moveToThread() 取决于当前特定线程中有多少客户端 - 这没有做任何改变(尽管我观察到速度变化很大)
  4. 生成工作进程使用

代码:

main.cpp:
#include <sched.h>

int startWorker(void *argv) {
int argc = 1;
QCoreApplication a(argc, (char **)argv);

Worker worker;
worker.Start();

return a.exec();
}

in main():
...
long stack[16 * 1024];
clone(startWorker, (char *)stack + sizeof(stack) - 64, CLONE_FILES, (void *)argv);

然后在主进程中启动 QLocalServer 并将 socketDescriptors 从 incomingConnection(int socketDescriptor) 传递给工作进程。它工作正常,但下载速度仍然很慢。

也试过:

  1. fork() - incomingConnection() 中的进程 - 这几乎杀死了服务器:)
  2. 为每个客户端创建单独的线程 - 速度降至 50-100 KB/s
  3. 将 QThreadPool 与 QRunnable 结合使用——没有区别

我正在使用 Qt 4.8.1

我没有想法了。

它是与 Qt 相关还是与服务器配置有关?

或者我应该使用不同的语言/框架/服务器?我需要为文件提供服务的 TCP 服务器,但我还需要在数据包之间执行一些特定任务,因此我需要自己实现该部分。

最佳答案

您的磁盘读取是阻塞操作,它们将停止任何处理,包括处理新网络连接等。您的磁盘也具有有限的 I/O 吞吐量,您可以使它饱和。您可能不希望您的磁盘停止应用程序的其余部分。我认为 Qt 在这里没有任何问题——除非您运行分析器并显示 Qt 的 CPU 消耗过多,或者 Qt 以某种方式在事件队列上发生锁争用(这些是唯一重要的问题) ).

你应该在 QObjects 之间拆分你的处理,如下所示:

  1. 接受传入连接。

  2. 处理套接字的写入和读取。

  3. 处理传入的网络数据并发出任何非文件回复。

  4. 从磁盘读取和写入网络。

当然 #1 和 #2 是现有的 Qt 类。

你必须写#3 和#4。您可以将#1 和#2 移动到它们之间共享的一个线程中。 #3 和#4 应该分布在多个线程中。 #3 的实例应该为每个事件连接创建。然后,当发送文件数据时,#3 实例化#4。 #4 可用的线程数应该是可调的,您可能会发现针对特定工作负载有一个最佳设置。您可以以循环方式在它们的线程中实例化#3 和#4。由于磁盘访问是阻塞的,用于 #4 的线程应该是独占的,不用于其他任何事情。

当写入缓冲区中剩余的数据少于一定数量时,#4 对象应该进行磁盘读取。这个数量可能不应该为零——如果可能的话,您希望让这些网络接口(interface)始终处于忙碌状态,并且用完要发送的数据是让它们空闲的一种可靠方法。

因此,我至少看到了以下您需要进行基准测试的可调参数:

  1. minNetworkWatermark - 套接字传输缓冲区中的最低水位。当要写入的字节数少于该数量时,您从磁盘读取并写入套接字。

  2. minReadSize - 最小磁盘读取大小。读取的文件将是 qMax(minNetworkWatermark - socket->bytesToWrite(), minReadSize)。

  3. numDiskThreads - #4 对象移动到的线程数。

  4. numNetworkThreads - #3 对象移动到的线程数。

您需要在不同的机器上进行基准测试,以了解运行速度以及调整的效果。从您的开发机器启动基准测试,无论是台式机还是笔记本电脑。由于它是您的日常主力,如果它的性能出现问题,您可能会很快注意到。

关于c++ - 具有大量并发客户端的慢速 QTcpServer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10270585/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com