如果 QTcpServer 尚未关闭,则从 QTcpSocket 读取失败
Reading from QTcpSocket fails if QTcpServer has not been closed
这是一个简单的最小工作示例,它重现了我觉得奇怪的行为(可能是由于我对某些事情的误解):
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 headers) 被读取并显示在标签中。
只有我将tcpServer->close();
放在acceptConnection()
槽的末尾(而不是在readClientData()
中),数据才能正常读取(标签显示header:GET /HTTP/1.1
等)。
完全不明白:为什么服务器要停止监听第一次连接才能正常读取数据?
在 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)
,稍后它将调用插槽 [=15] =] 与 (0x7fb1e4007600)
相关联,但您将读取 (0x559d7f3cb830)
的数据,即使该调用将丢失,也没有数据,然后第三个套接字将出现,并且它将对第二个套接字执行相同的操作.这就是他的方法失败的原因
另一方面,我的方法不会覆盖变量套接字,因为它不保存它,而是直接将其放入槽中
总而言之,如果具有调用槽的信号的对象有多个,在这种情况下,最好使用 sender() 获取发出信号的对象。
但其中一些(无论出于何种原因)将 return 来自 readClientData() 中的 sender() 的空指针?
如果它是一个槽可能不是,但它可能会发生,例如你可以直接调用 readClientData() 这样就不会有 sender() 或对任何其他对象的引用,所以为了安全我验证它。
这是一个简单的最小工作示例,它重现了我觉得奇怪的行为(可能是由于我对某些事情的误解):
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 headers) 被读取并显示在标签中。
只有我将tcpServer->close();
放在acceptConnection()
槽的末尾(而不是在readClientData()
中),数据才能正常读取(标签显示header:GET /HTTP/1.1
等)。
完全不明白:为什么服务器要停止监听第一次连接才能正常读取数据?
在 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)
,稍后它将调用插槽 [=15] =] 与 (0x7fb1e4007600)
相关联,但您将读取 (0x559d7f3cb830)
的数据,即使该调用将丢失,也没有数据,然后第三个套接字将出现,并且它将对第二个套接字执行相同的操作.这就是他的方法失败的原因
另一方面,我的方法不会覆盖变量套接字,因为它不保存它,而是直接将其放入槽中
总而言之,如果具有调用槽的信号的对象有多个,在这种情况下,最好使用 sender() 获取发出信号的对象。
但其中一些(无论出于何种原因)将 return 来自 readClientData() 中的 sender() 的空指针?
如果它是一个槽可能不是,但它可能会发生,例如你可以直接调用 readClientData() 这样就不会有 sender() 或对任何其他对象的引用,所以为了安全我验证它。