客户端每隔10ms向服务器发送一次数字字符串,从0开始。

- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include
- #include
- #include
- #include
- namespace Ui {
- class MainWindow;
- }
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- private:
- QTcpSocket *tcpClient; //socket
- QLabel *LabSocketState; //状态栏显示标签
-
- QString getLocalIP();//获取本机IP地址
- protected:
- void closeEvent(QCloseEvent *event);
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
-
- private slots:
- //自定义槽函数
- void onConnected();
- void onDisconnected();
- void onSocketStateChange(QAbstractSocket::SocketState socketState);
- void onSocketReadyRead();//读取socket传入的数据
- //
- void on_actConnect_triggered();
-
- void on_actDisconnect_triggered();
-
- void on_actClear_triggered();
-
- void on_pushButton_clicked();
-
- void send_msg();
-
- private:
- Ui::MainWindow *ui;
- QTimer* timer;
- };
-
- #endif // MAINWINDOW_H
关键:
QTimer* timer;
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
-
- #include
- #include
- #include
- QString MainWindow::getLocalIP()
- {
- QString hostName=QHostInfo::localHostName();//本地主机名
- QHostInfo hostInfo=QHostInfo::fromName(hostName);
- QString localIP="";
-
- QList
addList=hostInfo.addresses();// -
- if (!addList.isEmpty())
- for (int i=0;i
count();i++) - {
- QHostAddress aHost=addList.at(i);
- if (QAbstractSocket::IPv4Protocol==aHost.protocol())
- {
- localIP=aHost.toString();
- break;
- }
- }
- return localIP;
- }
- void MainWindow::closeEvent(QCloseEvent *event)
- {
- if (tcpClient->state()==QAbstractSocket::ConnectedState)
- tcpClient->disconnectFromHost();
- event->accept();
- }
-
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
-
- tcpClient=new QTcpSocket(this); //创建socket变量
-
- timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(send_msg()));
-
- LabSocketState=new QLabel("Socket状态:");//状态栏标签
- LabSocketState->setMinimumWidth(250);
- ui->statusBar->addWidget(LabSocketState);
-
- QString localIP=getLocalIP();//本机IP
- this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);
- ui->comboServer->addItem(localIP);
-
-
- connect(tcpClient,SIGNAL(connected()),this,SLOT(onConnected()));
- connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
-
- connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
- this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
- connect(tcpClient,SIGNAL(readyRead()),
- this,SLOT(onSocketReadyRead()));
- }
-
- MainWindow::~MainWindow()
- {
- delete ui;
- }
-
- void MainWindow::onConnected()
- { //connected()信号槽函数
- ui->plainTextEdit->appendPlainText("**已连接到服务器");
- ui->plainTextEdit->appendPlainText("**peer address:"+
- tcpClient->peerAddress().toString());
- ui->plainTextEdit->appendPlainText("**peer port:"+
- QString::number(tcpClient->peerPort()));
- ui->actConnect->setEnabled(false);
- ui->actDisconnect->setEnabled(true);
- }
-
- void MainWindow::onDisconnected()
- {//disConnected()信号槽函数
- ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");
- ui->actConnect->setEnabled(true);
- ui->actDisconnect->setEnabled(false);
- }
-
- void MainWindow::onSocketReadyRead()
- {//readyRead()信号槽函数
- while(tcpClient->canReadLine())
- ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
- }
-
- void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
- {//stateChange()信号槽函数
- switch(socketState)
- {
- case QAbstractSocket::UnconnectedState:
- LabSocketState->setText("scoket状态:UnconnectedState");
- break;
- case QAbstractSocket::HostLookupState:
- LabSocketState->setText("scoket状态:HostLookupState");
- break;
- case QAbstractSocket::ConnectingState:
- LabSocketState->setText("scoket状态:ConnectingState");
- break;
-
- case QAbstractSocket::ConnectedState:
- LabSocketState->setText("scoket状态:ConnectedState");
- break;
-
- case QAbstractSocket::BoundState:
- LabSocketState->setText("scoket状态:BoundState");
- break;
-
- case QAbstractSocket::ClosingState:
- LabSocketState->setText("scoket状态:ClosingState");
- break;
-
- case QAbstractSocket::ListeningState:
- LabSocketState->setText("scoket状态:ListeningState");
- }
- }
-
- void MainWindow::on_actConnect_triggered()
- {//连接到服务器
- QString addr=ui->comboServer->currentText();
- quint16 port=ui->spinPort->value();
- tcpClient->connectToHost(addr,port);
- // tcpClient->connectToHost(QHostAddress::LocalHost,port);
-
-
- }
-
- void MainWindow::on_actDisconnect_triggered()
- {//断开与服务器的连接
- if (tcpClient->state()==QAbstractSocket::ConnectedState)
- tcpClient->disconnectFromHost();
- }
-
- void MainWindow::on_actClear_triggered()
- {
- ui->plainTextEdit->clear();
- }
-
- void MainWindow::on_pushButton_clicked()
- {
- timer->start(10);
- }
- void MainWindow::send_msg()
- {
- static int m = 0;
- QString msg = QString::number(m);
-
- ui->plainTextEdit->appendPlainText("[out] " + msg);
-
- QByteArray str = msg.toUtf8();
- str.append('\n');
- tcpClient->write(str);
-
- m++;
- }
关键:
- timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(send_msg()));
- void MainWindow::on_pushButton_clicked()
- {
- timer->start(10);
- }
- void MainWindow::send_msg()
- {
- static int m = 0;
- QString msg = QString::number(m);
-
- ui->plainTextEdit->appendPlainText("[out] " + msg);
-
- QByteArray str = msg.toUtf8();
- str.append('\n');
- tcpClient->write(str);
-
- m++;
- }
修改:
- void MainWindow::on_pushButton_clicked()
- {
- timer->start(1);
- }
客户端每1ms向服务器发送一个数字,此时也可以。

如果客户端中:
- while (1)
- {
- QString msg = QString::number(1);
-
- //ui->plainTextEdit->appendPlainText("[out] " + msg);
- qDebug() << "[out] " + msg;
- QByteArray str = msg.toUtf8();
- str.append('\n');
- tcpClient->write(str);
- }
不断向服务器发送信息,此时页面是卡到一点都动不了,所以需要使用多线程来处理。

一种错误的写法:
- #include "workThread.h"
- #include
- #include
- #include
-
- workThread::workThread(QTcpSocket* tcpClient,QObject *parent)
- : QThread(parent)
- {
- qDebug()<<"workThread::workThread" << QThread::currentThread();
- this->tcpClient = tcpClient;
- timer = new QTimer(this);
- connect(timer, &QTimer::timeout, this, [=]() {
- ok = false;
- });
- timer->start(1000);
- }
-
- workThread::~workThread()
- {
- }
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- send_msg();
- }
- void workThread::send_msg()
- {
- qDebug() <<"send_msg():" << QThread::currentThread();
- while (1)
- {
- QString msg = QString::number(1);
- if (ok == false) {
- qDebug() << "[out] " + msg;
- ok = true;
- }
- QByteArray str = msg.toUtf8();
- str.append('\n');
- tcpClient->write(str);
- }
- }
workThread::workThread QThread(0xbde160)
run(): workThread(0xc744b0)
send_msg(): workThread(0xc744b0)
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread报错-CSDN博客
- class workThread : public QThread
- {
- Q_OBJECT
- signals:
-
- public:
- workThread(QObject *parent);
- ~workThread();
- protected:
- void run();
- private:
- QTcpSocket* tcpClient; //socket
- };
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- tcpClient = new QTcpSocket(this); //创建socket变量
-
- }
这样写会导致:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is workThread(0xc194f0), parent's thread is QThread(0xb7f940), current thread is workThread(0xc194f0)
关键在这句:
tcpClient = new QTcpSocket(this); //创建socket变量
this是主线程(0xb7f940)的,而当前线程是子线程(0xc194f0)。
tcpClient是子线程的(0xc194f0)。
不允许出现,父亲(this)与孩子(tcpClient)是不同线程的对象,这样的情况。
可以这样写:
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- tcpClient = new QTcpSocket; //创建socket变量
- }
另一种错误写法:
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- tcpClient = new QTcpSocket; //创建socket变量
- MainWindow* window = (MainWindow*)(parent());
- connect(window, &MainWindow::connectToHost, this, &workThread::connectToHost, Qt::QueuedConnection);
- qDebug() << "......";
- }
- void workThread::connectToHost(QString addr, quint16 port)
- {
- qDebug() << "workThread::connectToHost:" << QThread::currentThread();
- tcpClient->connectToHost(addr, port);
- }
run(): workThread(0x108f510)
......
workThread::connectToHost: QThread(0x100fa30)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x109c458), parent's thread is workThread(0x108f510), current thread is QThread(0x100fa30)
run()函数:工作线程(0x108f510)
connectToHost槽函数:主线程(0x100fa30)
有一个QObject子类对象:假设它的对象名为m。
它的parent()为QTcpSocket(0x109c458),QTcpSocket(0x109c458)所在的线程是工作线程(0x108f510),当前线程是主线程(0x100fa30)。
然后出现了这样的错误。
这种情况和上面的错误情况类似。
关键:
主线程中的对象m
子线程的对象n
m不可以是n的parent
n不可以是m的parent
另一种思路:
再封装一个QObject子类:
- #pragma once
-
- #include
- #include
- #include
- class socket : public QObject
- {
- Q_OBJECT
-
- public:
- socket(QObject *parent=nullptr);
- ~socket();
- public slots:
- void connectToHost(QString hostName, quint16 port);
- void send_msg();
- private:
- QTcpSocket* tcpClient; //socket
- QTimer* timer;
- bool ok;
- };
- #include "socket.h"
- #include
- #include
- socket::socket(QObject *parent)
- : QObject(parent)
- {
- qDebug() << "socket::socket:" << QThread::currentThread();
- timer = new QTimer(this);
- tcpClient = new QTcpSocket(this); //创建socket变量
- connect(timer, &QTimer::timeout, this, [=]() {
- ok = false;
- });
- timer->start(10000);
- }
-
- socket::~socket()
- {
- }
- void socket::connectToHost(QString addr, quint16 port)
- {
- qDebug() << "socket::connectToHost:" << QThread::currentThread();
- tcpClient->connectToHost(addr, port);
- }
- void socket::send_msg()
- {
- qDebug() <<"socket::send_msg():" << QThread::currentThread();
- while (1)
- {
- QString msg = QString::number(1);
- if (ok == false) {
- qDebug() << "[out] " + msg;
- ok = true;
- }
- QByteArray str = msg.toUtf8();
- str.append('\n');
- tcpClient->write(str);
- }
- }
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- tcp_client = new socket;
- MainWindow* window = (MainWindow*)(parent());
- connect(window, &MainWindow::connectToHost, tcp_client, &socket::connectToHost);
- qDebug() << "......";
-
- }
这样写的话,不会出现前面的问题。
但出现了新问题,window发送了connectToHost,而tcp_client没有执行connectToHost。
因为子线程没有开启事件循环。
【QT】跨线程的信号槽(connect函数)_qt跨线程信号槽-CSDN博客
QThread::exec();
- void workThread::run()
- {
- qDebug() <<"run():" << QThread::currentThread();
- tcp_client = new socket;
- MainWindow* window = (MainWindow*)(parent());
- connect(window, &MainWindow::connectToHost, tcp_client, &socket::connectToHost);
- qDebug() << "......";
-
- QThread::exec();
- }
此时主线程就可以跨线程向子线程通过信号槽发送信息啦。
思考:
connect(window, &MainWindow::connectToHost, tcp_client, &socket::connectToHost);
connectToHost槽函数在哪个线程执行,取决于tcp_client对象在哪个线程。
客户端:
多线程版本
在子线程中:每1ms向服务器发送一次数据。
(还有很多bug)
(绑定的资源文件对应这个版本)