您的位置:首页 > 科技 > 能源 > QT TCP多线程网络通信

QT TCP多线程网络通信

2024/12/23 7:43:38 来源:https://blog.csdn.net/weixin_50873490/article/details/140321423  浏览:    关键词:QT TCP多线程网络通信

学习目标: TCP网络通信编程

学习前置环境

运行环境:qt creator 4.12

QT TCP网络通信编程-CSDN博客

Qt 线程 QThread类详解-CSDN博客

学习内容

使用多线程技术实现服务端计数器

 核心代码

客户端

客户端:负责连接服务端,每次连接次数+1。以及连接的报错信息

#include "dialog.h"
#include "ui_dialog.h"
#include<QDebug>
Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog)
{ui->setupUi(this);ui->server_ip->setText("127.0.0.1");ui->server_port->setText("8888");clientSocket =new QTcpSocket;setWindowTitle("连接计数器客户端");//连接错误回调QObject::connect(clientSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, [this](QAbstractSocket::SocketError error){switch (error) {case QAbstractSocket::RemoteHostClosedError: // 远程主机关闭连接// QMessageBox::information(this, "提示", "远程主机关闭连接", QMessageBox::Yes);break;case QAbstractSocket::HostNotFoundError: // 找不到主机地址QMessageBox::information(this, "提示", "找不到主机地址", QMessageBox::Yes);break;case QAbstractSocket::ConnectionRefusedError: // 连接被对方拒绝(或者超时)QMessageBox::information(this, "提示", "连接被对方拒绝(或者超时)", QMessageBox::Yes);break;default:QMessageBox::information(this, "提示", tr("致命错误为:").arg(clientSocket->errorString()), QMessageBox::Yes);}ui->request->setEnabled(true);ui->close->setEnabled(true);});//当 socket 成功连接到服务器时,会发射 connected() 信号。connect(clientSocket,&QTcpSocket::connected,this,[this](){QString str ="已经连接到服务器端\n服务器端ip:"+clientSocket->peerAddress().toString()+"服务器端port:"+QString::number(clientSocket->peerPort());//QMessageBox::information(this, "提示",str , QMessageBox::Yes);ui->request->setEnabled(false);ui->close->setEnabled(true);QString msg=ui->currentv->text()+'\n';clientSocket->write(msg.toUtf8(),msg.length());int count =(ui->currentv->text().toUInt());ui->currentv->setNum(++count);});//当 socket 与服务器断开连接时,会发射 disconnected() 信号。connect(clientSocket,&QTcpSocket::disconnected,this,[this](){QString str ="已断开与服务器端的连接\n服务器端ip:"+clientSocket->peerAddress().toString()+"服务器端port:"+QString::number(clientSocket->peerPort());//QMessageBox::information(this, "提示",str , QMessageBox::Yes);clientSocket->close();});ui->request->setEnabled(true);ui->close->setEnabled(true);}Dialog::~Dialog()
{delete ui;
}void Dialog::on_request_clicked()
{clientSocket->connectToHost(ui->server_ip->text(),ui->server_port->text().toInt());}void Dialog::on_close_clicked()
{clientSocket->close(); // 取消已有的连接  后续触发断开回调ui->request->setEnabled(true);ui->close->setEnabled(false);
}

服务端

新连接请求类

通过继承重写的方式,实现新连接的回调操作。当然你也可以使用信号槽机制。如

connect(tcpServer, &QTcpServer::newConnection,对象,行为)。

实现功能:交给线程池处理,绑定实现连接断开前,把公用计数器+1操作,释放并清理资源。可以理解为绑定亡语操作,死后(连接断开)触发。

#ifndef TCPNEWCONNET_H
#define TCPNEWCONNET_H#include"writethread.h"
#include"dialog.h"class Dialog;class TcpNewConnet : public QTcpServer  //基于重写虚函数 实现新连接回调函数
{
Q_OBJECT
public:TcpNewConnet()=default;~TcpNewConnet()=default;TcpNewConnet(QObject *parent=0):QTcpServer(parent){dlgs =(Dialog*)parent;}
protected:// 当有新连接的时候会自动调用此函数void TcpNewConnet::incomingConnection(qintptr socketdescriptor){WriteThread *thread=new WriteThread(socketdescriptor,0);// 此处用于处理对话框显示统计访问次数信息connect(thread,&QThread::finished,dlgs,&Dialog::slotsdispFunc);connect(thread,&QThread::finished,thread,&QThread::deleteLater);thread->start(); // 通过执行这条语句来调用run()函数
}Dialog *dlgs;
};#endif // TCPNEWCONNET_H

多线程类

依然是通过重写的方式,注意点是Tcpsocket的生命周期,当过了{}作用域会自动释放这条连接。实现了客户端连接成功,再释放。

功能:创造一个连接,然后等这个连接死亡。触发亡语操作。

#include "writethread.h"WriteThread::WriteThread(int socketdescriptor,QObject *parent):QThread(parent),socketdescriptor(socketdescriptor)
{}void WriteThread::run(){ //多线程执行的函数//QTcpSocket* tcp =new QTcpSocket;    持久化连接{QTcpSocket tcp2; //离开作用域自动释放这条新连接QTcpSocket* tcp =& tcp2;if(!tcp->setSocketDescriptor(socketdescriptor)){emit myerror(tcp->error());  //触发自定义的error信号return;}qDebug()<<"run()";QByteArray data;QDataStream out(&data,QIODevice::WriteOnly);out.setVersion(QDataStream::Qt_5_12);tcp->write(data);}//tcp->disconnectFromHost(); //主动断开与远程主机的TCP连接。
}

主逻辑类

主要实现按钮开启和关闭服务器

#include "dialog.h"
#include "ui_dialog.h"
#include<QMessageBox>
Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog)
{ui->setupUi(this);setWindowTitle("连接计数器服务端");ui->server_ip->setText("127.0.0.1");ui->server_port->setText("8888");icount= 0;tcpserver=new TcpNewConnet(this);}Dialog::~Dialog()
{delete ui;
}void Dialog::slotsdispFunc(){ui->currentv->setText(tr("客户端请求%1次").arg(++icount));
}void Dialog::on_close_clicked()
{//先关闭所有socketif(!tcpserver){tcpserver->disconnect(); //用于断开 QTcpSocket 对象的所有信号与槽的连接。tcpserver->close();      //它会向对端发送 FIN 数据包,并等待对端的确认,完成 TCP 连接的正常关闭过程。//fin回调 已调用 tcpSocket->deleteLater(); //它不会立即删除对象,而是将其标记为待删除状态,等到当前事件循环结束后再执行删除操作。}if(tcpserver->isListening()){tcpserver->close();//不调用 deleteLater 为了下次再次开启ui->listen->setEnabled(true);ui->close->setEnabled(false);QMessageBox::critical(this,tr("提示"),tr("多线程服务器已关闭"));}}void Dialog::on_listen_clicked()
{QString ip(ui->server_ip->text());uint16_t port =ui->server_port->text().toUInt();if(!tcpserver->listen(QHostAddress(ip),port)){tcpserver->close();QMessageBox::critical(this,tr("提示"),tr("多线程服务器已关闭"));return;}QMessageBox::information(this,tr("提示"),tr("多线程服务器已经启动"));ui->listen->setEnabled(false);ui->close->setEnabled(true);
}

总结

通过继承重写和信号槽的方式,可以实现连接建立,断开,发送前,发送后等等操作绑定,重写需要去找指定的重写函数,而信号去找指定的信号名。信号槽机制当绑定多个的时候,是按照绑定的顺序执行,因为底层是信号队列,保证顺序。

如果对信号槽有兴趣,可以看我之前发布的qt 多线程和网络编程文章。

最后附上源代码链接
对您有帮助的话,帮忙点个star

 41-clinet-count · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

41-server-count · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com