(1)客户端每隔10ms向服务器发送一次数字字符串,从0开始。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
#include <QTimer>
namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECT
private:QTcpSocket *tcpClient; //socketQLabel *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 <QHostAddress>
#include <QHostInfo>
#include <QThread>
QString MainWindow::getLocalIP()
{QString hostName=QHostInfo::localHostName();//本地主机名QHostInfo hostInfo=QHostInfo::fromName(hostName);QString localIP="";QList<QHostAddress> addList=hostInfo.addresses();//if (!addList.isEmpty())for (int i=0;i<addList.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();//本机IPthis->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 <qapplication.h>
#include <qfiledialog.h>
#include <qdebug.h>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 <QObject>
#include <QTcpSocket>
#include <qtimer.h>
class socket : public QObject
{Q_OBJECTpublic:socket(QObject *parent=nullptr);~socket();
public slots:void connectToHost(QString hostName, quint16 port);void send_msg();
private:QTcpSocket* tcpClient; //socketQTimer* timer;bool ok;
};
#include "socket.h"
#include <qdebug.h>
#include <QThread>
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)
(绑定的资源文件对应这个版本)