这是一个简单的最小工作示例,它重现了我觉得很奇怪的行为(可能是由于我对某些事情的误解):
main.cpp
#include <QApplication>
#include "dialog.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Dialog dialog;
dialog.show();
return app.exec();
}
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
class QLabel;
class QTcpServer;
class QTcpSocket;
class Dialog : public QDialog {
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
public slots:
void acceptConnection();
void readClientData();
private:
QLabel *label;
QTcpServer *tcpServer;
QTcpSocket *socket;
};
#endif
dialog.cpp
#include <QtWidgets>
#include <QtNetwork>
#include "dialog.h"
Dialog::Dialog(QWidget *parent) : QDialog(parent) {
label = new QLabel("Server is listening...");
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(label);
setLayout(mainLayout);
tcpServer = new QTcpServer;
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
tcpServer->listen(QHostAddress::LocalHost, 10055);
}
void Dialog::acceptConnection() {
socket = tcpServer->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(readClientData()));
}
void Dialog::readClientData() {
QString data;
while (socket->canReadLine())
data += socket->readLine();
label->setText(data);
socket->close();
tcpServer->close();
tcpServer->deleteLater();
}
编译并运行后,出现对话框,然后转到浏览器,输入URL http://localhost:10055,然后...什么都没有。服务器接受连接,但是没有读取任何数据(HTTP标头),也没有在标签中显示该数据。
仅当我将tcpServer->close();
放在acceptConnection()
插槽的末尾(而不是放在readClientData()
中)时,数据才能正常读取(标签显示标题:{{1} }等。
我一点都不明白:为什么服务器应该停止监听第一个连接以正常读取数据?
答案 0 :(得分:0)
在Web请求中,执行了多个事务,因此连接不是唯一的,在您的情况下,您正在等待一个套接字的存在,但实际上可能同时存在多个套接字,这就是发生的情况在您的情况下,解决方案是使用sender()
动态处理套接字,以在readClientData()
中获得正确的套接字,如下所示:
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLabel;
class QTcpServer;
class QTcpSocket;
QT_END_NAMESPACE
class Dialog : public QDialog {
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
private slots:
void acceptConnection();
void readClientData();
private:
QLabel *label;
QTcpServer *tcpServer;
};
#endif
dialog.cpp
#include "dialog.h"
#include <QLabel>
#include <QTcpServer>
#include <QVBoxLayout>
#include <QTcpSocket>
Dialog::Dialog(QWidget *parent) : QDialog(parent) {
label = new QLabel("Server is listening...");
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(label);
tcpServer = new QTcpServer;
connect(tcpServer, &QTcpServer::newConnection, this, &Dialog::acceptConnection);
tcpServer->listen(QHostAddress::LocalHost, 10055);
}
void Dialog::acceptConnection() {
QTcpSocket *socket = tcpServer->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, this, &Dialog::readClientData);
}
void Dialog::readClientData() {
QTcpSocket *socket = dynamic_cast<QTcpSocket *>(sender());
if(socket){
QString data;
while (socket->canReadLine())
data += socket->readLine();
label->setText(data);
socket->close();
}
}
为什么服务器应该停止侦听第一个连接以正常读取数据?
因为在关闭服务器之前,您必须处理所有事件,例如获取数据,这样才能达到目的。
说明:
为了更好地理解该操作,我将添加一些照片:
void Dialog::acceptConnection() {
QTcpSocket *socket = tcpServer->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, this, &Dialog::readClientData);
qDebug()<< __PRETTY_FUNCTION__<< socket;
}
void Dialog::readClientData() {
QTcpSocket *socket = dynamic_cast<QTcpSocket *>(sender());
if(socket){
QString data;
while (socket->canReadLine())
data += socket->readLine();
qDebug()<< __PRETTY_FUNCTION__<< socket<<data;
label->setText(data);
socket->close();
}
}
输出:
void Dialog::acceptConnection() QTcpSocket(0x7fb1e4007600)
void Dialog::acceptConnection() QTcpSocket(0x559d7f3cb830)
void Dialog::readClientData() QTcpSocket(0x7fb1e4007600) "GET / HTTP/1.1\r\nHost: localhost:10055\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\r\nDNT: 1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hu;q=0.8\r\n\r\n"
void Dialog::readClientData() QTcpSocket(0x559d7f3cb830) "GET / HTTP/1.1\r\nHost: localhost:10055\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\r\nDNT: 1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hu;q=0.8\r\n\r\n"
void Dialog::acceptConnection() QTcpSocket(0x7fb1e40071e0)
void Dialog::readClientData() QTcpSocket(0x7fb1e40071e0) "GET / HTTP/1.1\r\nHost: localhost:10055\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\r\nDNT: 1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: en-US,en;q=0.9,hu;q=0.8\r\n\r\n"
如果您注意到已经创建了2个套接字,为什么还要创建2个套接字?因为浏览器可以保留多次。
在您的第一个代码中,套接字变量将首先使用(0x7fb1e4007600)
的值,然后第二个套接字将其设置为(0x559d7f3cb830)
,一段时间后它将调用插槽readClientData()
与(0x7fb1e4007600)
关联的数据,但是即使该调用将丢失,您仍将读取(0x559d7f3cb830)
的数据,即使该数据将丢失,然后第三个套接字也会出现,并且对第二个套接字也将执行相同的操作。这就是为什么他的方法失败了
另一方面,我的方法不会覆盖变量套接字,因为它不保存变量,而是直接将其放入插槽中
因此,总而言之,如果具有调用插槽的信号的对象是多个,则在这种情况下,最好使用sender()获取发出它的对象。
,但是其中某些(无论出于何种原因)会从readClientData()中的sender()返回空指针?
如果它可能不是插槽,但是可能会发生,例如,您可以直接调用readClientData(),这样就不会有sender()或对任何其他对象的引用,因此出于安全性考虑,我对此进行了验证。