注意点
QTcpSocket是Qt框架中的网络通信类,它使用QSocketNotifier实现异步通信。在Qt中,QSocketNotifier是一个用于处理套接字事件的类。
在使用QTcpSocket时,如果将读写套接字的操作放在线程中进行,则可能会出现"QSocketNotifier: socket notifiers cannot be enabled from another thread"的错误。这是因为QSocketNotifier是线程不安全的,它只能在创建它的线程中使用。
要解决这个问题,你可以使用Qt的信号和槽机制在线程之间传递数据。
具体步骤如下:
1.在主线程中创建QTcpSocket对象
2.在线程中创建一个新的QObject对象
3.将QObject对象的信号连接到QTcpSocket的readyRead()信号
4.在QObject对象的槽函数中读取数据
5.将QObject对象移动到线程中
这样,当QTcpSocket可以读取数据时,它会发出readyRead()信号,这将触发QObject对象的槽函数,从而在线程中读取数据。
#include <QTcpSocket>
#include <QDataStream>
class TcpClient : public QObject
{
Q_OBJECT
public:
TcpClient(QObject *parent = nullptr);
~TcpClient();
void connectToServer(const QString &hostName, quint16 port);
void disconnectFromServer();
void sendData(const QByteArray &data);
signals:
void connected();
void disconnected();
void error(QAbstractSocket::SocketError socketError);
void dataReceived(const QByteArray &data);
private slots:
void onConnected();
void onDisconnected();
void onError(QAbstractSocket::SocketError socketError);
void onReadyRead();
private:
QTcpSocket *m_tcpSocket;
QDataStream m_in;
};
TcpClient::TcpClient(QObject *parent) : QObject(parent)
{
m_tcpSocket = new QTcpSocket(this);
m_in.setDevice(m_tcpSocket);
m_in.setVersion(QDataStream::Qt_5_15);
connect(m_tcpSocket, &QTcpSocket::connected, this, &TcpClient::onConnected);
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);
connect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &TcpClient::onError);
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
}
TcpClient::~TcpClient()
{
m_tcpSocket->disconnectFromHost();
m_tcpSocket->waitForDisconnected();
}
void TcpClient::connectToServer(const QString &hostName, quint16 port)
{
m_tcpSocket->connectToHost(hostName, port);
}
void TcpClient::disconnectFromServer()
{
m_tcpSocket->disconnectFromHost();
}
void TcpClient::sendData(const QByteArray &data)
{
m_tcpSocket->write(data);
}
void TcpClient::onConnected()
{
emit connected();
}
void TcpClient::onDisconnected()
{
emit disconnected();
}
void TcpClient::onError(QAbstractSocket::SocketError socketError)
{
emit error(socketError);
}
void TcpClient::onReadyRead()
{
QByteArray data;
m_in >> data;
emit dataReceived(data);
}
如何必须这样做 【new QTcpSocket在主线程,读写socket在另外的线程】
如果你必须将socket的读写操作放在线程中进行,则可以使用Qt的事件机制来解决这个问题。
在Qt中,可以使用QCoreApplication::postEvent()函数在主线程中发送事件,并使用QObject::event()函数在线程中处理事件。
具体步骤如下:
1.在主线程中创建QTcpSocket对象
2.在线程中创建一个新的QObject对象
3.在主线程中使用QCoreApplication::postEvent()函数发送事件
4.在QObject对象的event()函数中读取数据
5.将QObject对象移动到线程中
这样,当QTcpSocket可以读取数据时,主线程会发送事件到线程中,并触发QObject对象的event()函数,从而在线程中读取数据。
#include <QTcpSocket>
#include <QThread>
#include <QObject>
#include <QCoreApplication>
class SocketEvent : public QEvent
{
public:
SocketEvent(QTcpSocket *socket)
: QEvent(QEvent::Type(QEvent::User + 1)), m_socket(socket)
{
}
QTcpSocket *socket() const
{
return m_socket;
}
private:
QTcpSocket *m_socket;
};
class SocketThread : public QThread
{
Q_OBJECT
public:
SocketThread(QTcpSocket *socket)
: m_socket(socket)
{
}
protected:
void run() override
{
// 创建QObject对象
QObject *receiver = new QObject;
// 将QObject对象移动到线程中
receiver->moveToThread(this);
// 开始执行线程
QThread::exec();
}
private:
QTcpSocket *m_socket;
}
// 在QObject对象的event()函数中读取数据
bool QObject::event(QEvent *event)
{
if (event->type() == QEvent::User + 1) {
SocketEvent *socketEvent = static_cast<SocketEvent *>(event);
QTcpSocket *socket = socketEvent->socket();
QByteArray data = socket->readAll();
// 处理读取的数据
return true;
}
return QObject::event(event);
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// 在主线程中创建QTcpSocket对象
QTcpSocket *socket = new QTcpSocket;
// 创建SocketThread对象
SocketThread *thread = new SocketThread(socket);
// 开始执行线程
thread->start();
// 连接QTcpSocket的readyRead()信号
QObject::connect(socket, &QTcpSocket::readyRead, [thread, socket]() {
// 使用QCoreApplication::postEvent()函数发送事件
QCoreApplication::postEvent(thread, new SocketEvent(socket));
});
return app.exec();
}