QT TCP多线程网络通信

news2024/9/20 21:27:13

学习目标: 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()
{
        //先关闭所有socket
       if(!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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1925828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sklearn之神经网络学习算法

文章目录 什么是神经网络人工神经网络的结构输入层输出层隐含层神经元的链接 近几年深度学习还是比较火的&#xff0c;尤其是在大语言模型之后&#xff0c;在本质上深度学习网络就是层数比较多的神经网络。sklearn并不支持深度学习&#xff0c;但是支持多层感知机&#xff08;浅…

安全测试理论

安全测试理论 什么是安全测试&#xff1f; 安全测试&#xff1a;发现系统安全隐患的过程安全测试与传统测试区别 传统测试&#xff1a;发现bug为目的 安全测试&#xff1a;发现系统安全隐患什么是渗透测试 渗透测试&#xff1a;已成功入侵系统为目标的的攻击过程渗透测试与安全…

自动驾驶事故频发,安全痛点在哪里?

大数据产业创新服务媒体 ——聚焦数据 改变商业 近日&#xff0c;武汉城市留言板上出现了多条关于萝卜快跑的投诉&#xff0c;多名市民反映萝卜快跑出现无故停在马路中间、高架上占最左道低速行驶、转弯卡着不动等情况&#xff0c;导致早晚高峰时段出现拥堵。萝卜快跑是百度 A…

配置与管理Samba服务器(详细教程)

目录 一、基础理论 二、samba工作流程 三、项目实训 3.1目的 3.2准备工作 3.2.1服务器安装samba服务软件包 3.2.2客户端安装软件包 3.3配置Samba服务 3.3.1开启Samba服务&#xff0c;并设置开启自启动 3.3.2创建共享文件夹 3.3.3创建群组 3.3.4修改文件用户权限 3.3.5修改…

LabVIEW人工模拟肺控制系统开发

开发了一种创新的主被动一体式人工模拟肺模型&#xff0c;通过LabVIEW开发的上位机软件&#xff0c;实现了步进电机驱动系统的精确控制和多种呼吸模式的模拟。该系统不仅能够在主动呼吸模式下精确模拟快速呼吸、平静呼吸和深度呼吸&#xff0c;还能在被动模式下通过PID控制实现…

训练CDN基础代码

文章目录 时间整体流程训练细节小结 时间 从开始在平台上搭建到现在可以在平台上训练已经4天了 有GPU平台一般是autoDL平台&#xff0c;白嫖200元平台是&#xff1a;https://cloud.lanyun.net/ 整体流程 1.注册平台&#xff0c;以蓝耘为例子 卡从好变坏依次是&#xff1a;…

C语言:指针详解(5)

目录 一、sizeof()函数和strlen()函数的对比 1.sizeof()函数 2.strlen()函数 3.sizeof()函数和strlen()函数的对比 二、数组和指针笔试试题解析 1.一维数组 2.字符数组 &#xff08;1&#xff09;代码1 &#xff08;2&#xff09;代码2 &#xff08;3&#xff09;代码…

【数组、特殊矩阵的压缩存储】

目录 一、数组1.1、一维数组1.1.1 、一维数组的定义方式1.1.2、一维数组的数组名 1.2、二维数组1.2.1、二维数组的定义方式1.2.2、二维数组的数组名 二、对称矩阵的压缩存储三、三角矩阵的压缩存储四、三对角矩阵的压缩存储五、稀疏矩阵的压缩存储 一、数组 概述&#xff1a;数…

香橙派AIpro:体验强劲算力,运行ROS系统

文章目录 前言一、香橙派AIpro开箱及功能介绍1.1香橙派AIpro开箱1.2香橙派AIpro功能介绍 二、香橙派AIpro资料下载及环境搭建2.1资料下载2.2环境搭建2.3使用串口启动进入开发板2.4使用HDMI线接入屏幕启动 三、部署ROS系统四、香橙派AIpro的使用和体验感受 前言 本篇文章将带体…

sip协议栈简介

SIP协议栈简介 SIP协议栈流程 数据链路层&#xff1a;当SIP消息从网络中传输到达TCP/IP协议栈时&#xff0c;首先被接收到的是数据链路层的数据帧。数据链路层会对数据帧进行解封装&#xff0c;得到网络层的IP数据报。 网络层&#xff1a;网络层会对IP数据报进行解析&#xf…

js实现 JSON数据格式化的两种方法

本次方法不使用JS库直接采用原生JS 完整HTML代码如下&#xff0c;您可以复制代码然后&#xff0c;新建一个.html的网页进行保存即可体验 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><b…

Xcode 16 beta3 真机调试找不到 Apple Watch 的尝试解决

很多小伙伴们想用 Xcode 在 Apple Watch 真机上调试运行 App 时却发现&#xff1a;在 Xcode 设备管理器中压根找不到对应的 Apple Watch 设备。 大家是否已将 Apple Watch 和 Mac 都重启一万多遍了&#xff0c;还是束手无策。 Apple Watch not showing in XCodeApple Watch wo…

android13 文件管理器无法安装apk 奔溃问题

总纲 android13 rom 开发总纲说明 目录 1.前言 2.我们简单写个apk测试下 3.排查客户apk 4.frameworks源码排查 5.编译验证 6.彩蛋 1.前言 客户提供的文件管理apk不能安装apk文件,一点击就奔溃。 2.我们简单写个apk测试下 private void installApk(File apkFile) {i…

从0开始学习informer

目录 informer特点informer原理attention计算KL散度 backbone网络部分encoder输入输出部分embadding这里就不讲了 和transfomer一样EncoderStack decoder部分接下来就是最关键的结构 关于如何将输入经过注意力得到结果 结束&#xff0c;代码会放到下一篇讲 这里是原理 informer…

[Vulnhub] Sedna BuilderEngine-CMS+Kernel权限提升

信息收集 IP AddressOpening Ports192.168.8.104TCP:22, 53, 80, 110, 111, 139, 143, 445, 993, 995, 8080, 55679 $ nmap -p- 192.168.8.104 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 …

PTX入门教程与实战

PTX入门教程 官方文档的目录结构 1 PTX指令 官方文档链接 1.1 指令形式 指令的操作数个数从0-4不等&#xff0c;其中d代表的是目的操作数&#xff0c;a,b,c是源操作数 p opcode;p opcode a;p opcode d, a;p opcode d, a, b;p opcode d, a, b, c;2 编程模型 2.…

人工智能算法工程师(中级)课程9-PyTorch神经网络之全连接神经网络实战与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程9-PyTorch神经网络之全连接神经网络实战与代码详解。本文将给大家展示全连接神经网络与代码详解&#xff0c;包括全连接模型的设计、数学原理介绍&#xff0c;并从手写数字识别到猫狗识…

JVM监控及诊断工具-命令行篇-jstat命令介绍

JVM监控及诊断工具-命令行篇01-jstat&#xff1a;查看JVM统计信息 一 基本情况二 基本语法2.1 option参数1. 类装载相关的&#xff1a;2. 垃圾回收相关的-gc&#xff1a;显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息…

基于51单片机的多路报警器Protues仿真设计

一、设计背景 随着社会的发展和技术的进步&#xff0c;安全问题越来越受到重视。各种工业设施、家庭、商业场所以及公共场所的安全保障成为了重点。报警器作为安全防护系统的重要组成部分&#xff0c;在预防和及时应对各种突发事件中发挥着至关重要的作用。传统的报警器通常在功…

【C++】哈希(散列)表

目录 一、哈希表的基本概念1.哈希的概念2.哈希冲突2.1 哈希函数2.2 哈希冲突的解决办法2.2.1 闭散列2.2.2 开散列 二、哈希表的实现1.闭散列的实现1.1 闭散列的结构1.2 闭散列的插入1.3 闭散列的删除1.4 闭散列的查找 2.开散列的实现2.1 key值不能取模的情况2.2 开散列的结构2.…