我在使用QT-Framework为自己编写一些网络应用程序时偶然发现了一个问题 . 我非常喜欢信号/插槽系统,但我感觉我在这里遇到了竞争状态 .
我的服务器正在快速发送小的“电报”,因为它们不值得自己的TCP数据包(很好) .
在客户端,我使用套接字readyRead-slot并处理我的处理程序槽“socketHasData”中的incomming数据 .
问题是,当执行插槽内的代码时,似乎不会重新发出信号 .
我绕了它一个循环,这有点帮助(我收到更多的电报) . 看起来如果退出循环但不退出插槽然后接收数据,则跳过信号 . 执行从插槽返回后,我的客户端卡住并等待数据堆积在套接字缓冲区中 .
我能做什么?是否可以根据QT进行此次活动?我明白了什么问题吗?如何以这种方式实现基于电报的数据传输?
这里的问题似乎已经解决了:How to make sure that readyRead() signals from QTcpSocket can't be missed?但我没有提到上述错误:
-
我没有调用waitForReadyRead
-
我没有进入任何事件循环
-
我不使用除主线程之外的任何线程
并且仍然遇到问题 .
以下是一些具有相关行为的示例代码:
服务器
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTcpServer *tcpServer = new QTcpServer(NULL);
tcpServer->listen(QHostAddress("127.0.0.1"), 5573);
tcpServer->waitForNewConnection(-1);
QTcpSocket *socket = tcpServer->nextPendingConnection();
QByteArray telegram;
for (int i = 0; i < 10000; ++i) {
telegram.clear();
QString message = "Test number " + QString::number(i);
// Each "telegram" is just that string and an incrementing number.
telegram.append(static_cast<char>(message.length()));
telegram.append(message.toLocal8Bit());
socket->write(telegram);
}
socket->flush();
socket->close();
tcpServer->close();
return a.exec();
}
客户
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::on_pushButton_clicked() {
this->socket = new QTcpSocket(this);
this->socket->connectToHost("127.0.0.1", 5573);
connect(this->socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
}
void MainWindow::socketHasData() {
do {
QByteArray header = this->socket->read(1);
int length = header.at(0);
while (this->socket->bytesAvailable() < length);
QByteArray payload = this->socket->read(length);
qDebug() << "[RX]" << QString::fromLocal8Bit(payload);
} while (this->socket->atEnd() == false); // I added the loop, which helps somewhat
}
Client-Header
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void socketHasData();
private:
Ui::MainWindow *ui;
QTcpSocket *socket;
};
#endif // MAINWINDOW_H
1 回答
如果你的数据是"pre-packaged",你可以很容易地被你识别,为什么不简单地使用QTimer并将
QTimer::timeout()
连接到某个slot_processData(QByteArray incoming)
插槽?可以找到合理的超时(使用套接字通常是10-50毫秒安全) .分析
slot_processData
插槽中的可用数据,将部分数据块保留在"global"字节数组中以获取传入数据 .这对于"known"数据实际上非常有效 . 我将它用于
QSerialPort
和QTcpSocket
两者 .QSerialPort
的QTimer
超时值可能更难以确定 . 我发现通常需要对该值进行用户配置控制以考虑旧计算机或超载计算机等 .ALTERNATELY 如果数据在"chunk"中相对较小,请使用
readyRead
槽每次评估所有数据,并将完整的块发送到处理槽/处理程序 . 我在串口实现中使用它非常多,因为硬件设备通常具有短指示或回复值 . 它消除了QTimer
问题 .