【QT】TCP

news2024/11/22 14:58:35

目录

核心API

示例:服务器和客户端信息互发

服务器代码实现

第一步:创建QTcpServer对象的实例

第二步:绑定信号槽,处理新的连接

第三步:绑定并监听端口号

客户端代码实现

第一步:创建socket对象的实例

第二步:和服务器建立连接

第三步:连接信号槽,处理响应 

第四步:等待连接结果,确认是否连接成功 

最终效果


UDP 无连接,不可靠传输,面向数据包,全双工

TCP 有连接,可靠传输,面向字节流,半双工

核心API

核心类是两个: QTcpServer QTcpSocket

QTcpServer ⽤于监听端⼝, 和获取客户端连接

名称
类型
说明
对标原⽣ API
listen(const QHostAddress&, quint16 port)
⽅法
绑定指定的地址和端⼝号, 并开始监听
bind 和 listen
nextPendingConnection()
⽅法
从系统中获取到⼀个已经建⽴好的 tcp 连接.
返回⼀个 QTcpSocket , 表⽰这个
客⼾端的连接.
通过这个 socket 对象完成和客⼾端 之间的通信
accept
newConnection
信号
有新的客⼾端建⽴连接好之后触发.
⽆ (但是类似于 IO 多路复⽤
中的通知机制)
QTcpSocket 用户客⼾端和服务器之间的数据交互
名称类型说明对标原⽣ API
readAll()
⽅法
读取当前接收缓冲区中的所有数据.
返回 QByteArray 对象.
read
write(const QByteArray& )
⽅法
把数据写⼊ socket 中.
write
deleteLater
⽅法
暂时把 socket 对象标记为⽆效. Qt
会在下个事件循环中析构释放该对
象.
⽆ (但是类似于 "半⾃动化的 垃圾回收")
readyRead
信号
有数据到达并准备就绪时触发
⽆ (但是类似于 IO 多路复⽤
中的通知机制)
disconnected
信号
连接断开时触发
⽆ (但是类似于 IO 多路复⽤
中的通知机制)

示例:服务器和客户端信息互发

服务器代码实现

首先区分QTcpServer 和 QTcpSocket,我们需要先使用QTcpServer来监听端⼝, 和获取客户端连接。

第一步:创建QTcpServer对象的实例

tcpServer = new QTcpServer(this代码);

第二步:绑定信号槽,处理新的连接

connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);

processConnection方法 

void Widget::processConnection()

{

    //1.通过tcpserver拿到一个socket对象,通过这个对象来和客户端进行通信

    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();

    QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + " 客户端上线了!";

    ui->listWidget->addItem(log);

    //2.通过信号槽,来处理客户端发来请求的情况

    connect(clientSocket,&QTcpSocket::readyRead,this,[=](){

       // a) 读取请求数据,此处readAll返回的是QByteArray,通过赋值转成 QString

       QString request = clientSocket->readAll();

       // b) 根据请求处理响应

       const QString& response = process(request);

       // c) 把响应写回到客户端

       clientSocket->write(response.toUtf8());

       // d) 把上述信息记录到日志中

       QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]" + "req:" + request + ",resp:" + response;

    });

       ui->listWidget->addItem(log);

    //3. 通过信号槽,来处理客户端断开连接的情况

    connect(clientSocket,&QTcpSocket::disconnected,this,[=](){

        //a) 把断开连接的信息通过日志显示出来

       QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + " 客户端下线了!";

       ui->listWidget->addItem(log);

       //b) 手动释放clientSocket

       clientSocket->deleteLater();

    });

}

注意: 

  • 在Linux网络编程中,需要搞一个循环,循环的读取请求,循环的处理,在Qt中基于信号槽就不必循环了。每次客户端发来请求,都能触发readyRead信号。
  • 因为QTcpSocket是每个客户端都有一个这样的对象,存在n个,所以必须在客户端断开连接后手动释放,而QTcpServer和QUdpSocket只有一份
  • clientSocket->deleteLater();这个操作,不是立即销毁clientSocket,而是告诉Qt,下一轮事件循环时,再进行销毁操作。

第三步:绑定并监听端口号

    bool ret = tcpServer->listen(QHostAddress::Any,9090);

    if(!ret)

    {

        QMessageBox::critical(this,"服务器启动失败!",tcpServer->errorString());

        exit(1);

    }

完整代码:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void processConnection();
    QString process(const QString request);
private:
    Ui::Widget *ui;
    QTcpServer* tcpServer;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTcpSocket>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //修改窗口标题
    this->setWindowTitle("服务器");

    //2.创建QTcpServer的实例
    tcpServer = new QTcpServer(this);

    //3.通过信号槽,指定如何处理连接
    connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);

    //4.绑定并监听端口号
    bool ret = tcpServer->listen(QHostAddress::Any,9090);
    if(!ret)
    {
        QMessageBox::critical(this,"服务器启动失败!",tcpServer->errorString());
        exit(1);
    }
}

Widget::~Widget()
{
    delete ui;
}

void Widget::processConnection()
{
    //1.通过tcpserver拿到一个socket对象,通过这个对象来和客户端进行通信
    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
    QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + " 客户端上线了!";
    ui->listWidget->addItem(log);

    //2.通过信号槽,来处理客户端发来请求的情况
    connect(clientSocket,&QTcpSocket::readyRead,this,[=](){
       // a) 读取请求数据,此处readAll返回的是QByteArray,通过赋值转成 QString
       QString request = clientSocket->readAll();
       // b) 根据请求处理响应
       const QString& response = process(request);
       // c) 把响应写回到客户端
       clientSocket->write(response.toUtf8());
       // d) 把上述信息记录到日志中
       QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]" + "req:" + request + ",resp:" + response;
       ui->listWidget->addItem(log);
    });
       
    //3. 通过信号槽,来处理客户端断开连接的情况
    connect(clientSocket,&QTcpSocket::disconnected,this,[=](){
        //a) 把断开连接的信息通过日志显示出来
       QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + " 客户端下线了!";
       ui->listWidget->addItem(log);
       //b) 手动释放clientSocket
       clientSocket->deleteLater();
    });
}

QString Widget::process(const QString request)
{
    return request;
}

客户端代码实现

客户端不需要跟服务器一样监听端口,因此只需要创建socket即可

第一步:创建socket对象的实例

 tcpSocket = new QTcpSocket(this);

第二步:和服务器建立连接

 这个函数不会阻塞等待三次握手完成的(非阻塞函数)

tcpSocket->connectToHost("127.0.0.1",9090);

第三步:连接信号槽,处理响应 

 connect(tcpSocket,&QTcpSocket::readyRead,this,[=](){

       QString response = tcpSocket->readAll();

        ui->listWidget->addItem("服务器说:"+response);

    });

第四步:等待连接结果,确认是否连接成功 

    bool ret = tcpSocket->waitForConnected();

    if(!ret){

        QMessageBox::critical(this,"连接服务器",tcpSocket->errorString());

    }

完整代码: 

 widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket* tcpSocket;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //1.设置窗口标题
    this->setWindowTitle("客户端");

    //2.创建socket对象的实例
    tcpSocket = new QTcpSocket(this);

    //3.和服务器建立连接  这个函数不会阻塞等待三次握手完成的(非阻塞函数)
    tcpSocket->connectToHost("127.0.0.1",9090);

    //4. 连接信号槽,处理响应
    connect(tcpSocket,&QTcpSocket::readyRead,this,[=](){
       QString response = tcpSocket->readAll();
        ui->listWidget->addItem("服务器说:"+response);
    });

    //5.等待连接建立的结果,确认是否连接成功
    bool ret = tcpSocket->waitForConnected();
    if(!ret){
        QMessageBox::critical(this,"连接服务器",tcpSocket->errorString());
    }
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    //1.获取输入的内容
    const QString& text = ui->lineEdit->text();

    //2.发送数据给服务器
    tcpSocket->write(text.toUtf8());

    //3.把发送的信息显示到界面上
    ui->listWidget->addItem("客户端说:" + text);

    //4.清空输入框的内容
    ui->lineEdit->setText("");


}

最终效果

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

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

相关文章

【计算机网络】WireShark和简单http抓包实验

一&#xff1a;实验目的 1&#xff1a;熟悉WireShark的安装流程和界面操作流程。 2&#xff1a;学会简单http的抓取和过滤&#xff0c;并分析导出结果。 二&#xff1a;实验仪器设备及软件 硬件&#xff1a; Windows 2019操作系统的计算机等。 软件&#xff1a;WireShark、…

智能PDF转markdown

嘿&#xff0c;各位技术大咖们&#xff0c;今天我要给大家带来一个超酷的项目——“智能PDF转Markdown”&#xff0c;这可是数字化办公的神器&#xff0c;基于Marker技术&#xff0c;让PDF文档秒变Markdown&#xff0c;轻松应对RAG知识库构建等任务的挑战&#xff01; 打造了一…

Python | Leetcode Python题解之第299题猜数字游戏

题目&#xff1a; 题解&#xff1a; class Solution:def getHint(self, secret: str, guess: str) -> str:bulls 0cntS, cntG [0] * 10, [0] * 10for s, g in zip(secret, guess):if s g:bulls 1else:cntS[int(s)] 1cntG[int(g)] 1cows sum(min(s, g) for s, g in z…

学习笔记 韩顺平 零基础30天学会Java(2024.7.22)

P407 接口使用细节2 P407 接口课堂练习 对于最后一个的输出&#xff1a;B因为实现了A的接口&#xff0c;所以和继承一样&#xff0c;B可以访问A的变量 P409 接口VS继承 接口对单继承机制&#xff08;是指只能继承一个类&#xff09;进行了补充 也可以理解为&#xff0c;子类通过…

IndexError: list index out of range

IndexError: list index out of range 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主理人 擅长.net、C、python开发&#xff0c; 如果遇到技术问题&#xff0c;即…

锅总介绍CNCF主要目标、全景图及发展历史

一、CNCF简介 云原生计算基金会&#xff08;Cloud Native Computing Foundation&#xff0c;简称 CNCF&#xff09;是一个成立于 2015 年的非营利性组织&#xff0c;隶属于 Linux 基金会。CNCF 的主要目标是通过开源软件推动云原生计算技术的发展和普及&#xff0c;帮助企业更…

【C语言篇】C语言数据类型和变量

文章目录 C语言数据类型和变量1. 数据类型介绍1.1 字符型1.2 整形1.3 浮点型1.4 布尔类型1.5 各种类型数据长度1.5.1 sizeof操作符1.5.2 数据类型长度1.5.3 sizeof表达式不计算 2. signed和unsigned3. 数据类型的取值范围4. 变量4.1变量的创建4.2 变量的分类 5.强制类型转换 C语…

Redis:快速键值存储的入门指南

一、什么是Redis&#xff1f; Redis&#xff0c;全称为Remote Dictionary Server&#xff0c;是一种开源的、高性能的键值&#xff08;Key-Value&#xff09;存储系统。与传统的关系型数据库不同&#xff0c;Redis将数据主要存储在内存中&#xff0c;因此能够提供极低延迟的数…

网络传输层——UDP与TCP

前言&#xff1a; 1.国际网络体系结构&#xff1a; OSI模型: open system interconnect 理论模型 1977 国际标准化组织 各种不同体系结构的计算机能在世界范围内互联成网。 应用层:要传输的数据信息&#xff0c;如文件传输&#xff0c;电子邮件等…

【C语言】宏定义常量加 ; 的错误

我在使用宏定义常量定义二维数组的时候&#xff0c;编译器报错&#xff1a;应输入“]”&#xff0c;如下&#xff1a; 原因是宏定义不是C语言规定的语句&#xff0c;它的结尾不加 ; 。在上图的 int mine[EASY_ROWS][EASY_COLS]; 中&#xff0c;把 EASY_ROWS 替换为了 9;2; &…

Typora 以 Github 作为图床使用 PicGo 上传图片

本文简练快速介绍如标题所述的操作流程 文章目录 1.前言1.1 图床简述1.2 Github图床的优缺点1.2.1 优点1.2.2 缺点 2.下载PicGo3.Github访问加速4.用github创建图床服务器4.1 注册4.2 创建仓库 4.3 生成TOKEN令牌5.设置PicGo6.设置Typora7.完成 1.前言 1.1 图床简述 图床&…

车载音频记录

一、车载音频总线 2.8车载音频总线A2B Automotive Audio Bus_a2b总线-CSDN博客 传统的车载音频接口采用的是点对点模式&#xff0c;车身数字音频总线采用环型或者菊花链型总线。 A2B&#xff1a;Automotive Audio Bus 支持串联拓扑&#xff0c;即单个主机最多连接10个…

RedHat8安装Oracle19C

RedHat8安装Oracle19C 1、 更新yum源 更新yum源为阿里云镜像源&#xff1a; # 进入源目录 cd /etc/yum.repos.d/ # 删除 redhat 默认源 rm redhat.repo # 下载阿里云的centos7源 curl -O http://mirrors.aliyun.com/repo/Centos-8.repo # 替换 Centos-8.repo 中的 $releasev…

机器学习(二十二):精度和召回率

一、倾斜数据集 倾斜数据集&#xff1a;一个数据集中的正面和负面例子的比例非常不平衡&#xff0c;比如数据集中&#xff0c;结果为1的占比20%&#xff0c;结果为0的占比80% 例子&#xff1a;如果数据集的结果中只有0.5%是1&#xff0c;其余结果是0。有一个模型的预测准确度…

24.7.17数据结构|顺序表

目录 大O的工程意义&#xff1f; 线性表 引入&#xff1a; 主要掌握【代码实现】&#xff1a; 一、线性结构 1、逻辑描述 2、顺序表 1、如何定义结构 1&#xff09;静态顺序表 1&#xff09;动态顺序表 2、写代码 &#xff08;1&#xff09;【clion创建工程】 ​编…

Unity横板动作游戏 - 素材导入和整理

导入素材 编辑器布局 点击每个窗口右上角的三个点可以有更多的窗口选项。 在屏幕的右上角有一个菜单可以保存布局或读取已经报错的布局。 工具按钮 编辑器上的工具按钮在启动的时候是蓝色的&#xff0c;在不启动的时候是灰色的。 这个按钮将会决定场景中的物体是以锚点显示还…

大模型算法面试题(十三)

本系列收纳各种大模型面试题及答案。 1、微调后的模型出现能力劣化&#xff0c;灾难性遗忘是怎么回事 微调后的模型出现能力劣化&#xff0c;灾难性遗忘&#xff08;Catastrophic Forgetting&#xff09;是一个在机器学习领域&#xff0c;尤其是在深度学习和大模型应用中频繁出…

麒麟系统信创改造

麒麟系统信创改造 一、查看操作系统架构下载相应的依赖,压缩包1、查看Linux系统架构、CPU(1)uname -m(2)lscpu(3)cat /proc/cpuinfo(4)arch(5)getconf LONG_BIT(6)dmidecode2、根据Linux系统架构、CPU的差异进行下载相关依赖,看第二项二、以下是根据本系统的aarc…

Golang | Leetcode Golang题解之第297题二叉树的序列化与反序列化

题目&#xff1a; 题解&#xff1a; type Codec struct{}func Constructor() (_ Codec) {return }func (c Codec) serialize(root *TreeNode) string {if root nil {return "X"}left : "(" c.serialize(root.Left) ")"right : "("…

WordPress插件介绍页源码单页Html

源码介绍 WordPress插件介绍页源码单页Html源码&#xff0c;这是一款产品介绍使用页面&#xff0c;也可以用来做其他软件或者应用介绍下载页&#xff0c;界面简约美观&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器…