Qt网络编程——QTcpServer和QTcpSocket

文章目录

    • 核心API
    • TCP回显服务器
    • TCP回显客户端

核心API

QTcpServer用于监听端口和获取客户端连接

名称类型说明对标原生API
listen(const QHostAddress&, quint16 port)方法绑定指定的地址和端口号,并开始监听bind和listen
nextPendingConnection()方法从系统中获取到一个建立好的tcp连接
返回一个QTcpSocket,表示这个客户端的连接
通过这个socket对象完成和客户端之间的通信
accept
newCondition()信号有新的客户端建立好连接之后触发无(类似IO多路复用的通知机制)

QTcpSocket用于客户端和服务器之前的数据交互

tcp读取的数据是字节流,因此读取和返回的都是字节数组,这和udp的QNetworkDatagram数据报不一样

事件循环,可以简单理解为是Qt程序内部一个带有“生物钟”这样的东西,周期性执行一些逻辑

名称类型说明对标原生API
readAll()方法读取当前接收缓冲区的所有数据
返回QByteArray对象
read
write(const QByteArray&)方法将数据写入socket当中write
deleteLater方法暂时把socket对象标记为无效
Qt会在下一个事件循环中构造释放该对象
readyRead信号有数据到达并准备就绪时触发
disconnected信号连接断开时触发

QByteArray是字节数组,可以和QString互相转换

  • QString的构造函数可以把QByteArray转换成QString
  • QStringtoUtf8函数可以把QString转成QByteArray

TCP回显服务器

用这些接口,写一个回显服务器。

创建项目之后,如果要进行网络编程,第一步就是在.pro文件中加入network模块

image-20240925220250317

界面:

image-20240925220340130

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

  • 绑定和监听端口号一定是要等准备工作做完之后再进行,比如说如何处理连接、如何处理请求等…

  • 需要手动释放clientSocket,因为它是每个客户端都有这样一个对象,而QTcpServerQUdpServer都是只有一份。如果不对断开连接的客户端进行释放,累计的客户端会越来越多,这会导致两个问题:文件描述符泄漏内存泄漏

    也不能直接delete clientSocket,因为当前槽函数主要是围绕clientSocket来进行操作的,一旦delete,其他逻辑就无法使用clientSocket,使用要保证delete操作是最后一步,而且不会被return或者抛出异常给跳过。

    Qt提供了deleteLater,不是立即销毁,而是告诉Qt,下一轮事件循环中,再进行上述销毁操作。

    槽函数是在事件循环中进行的,进入下一轮事件循环表明上一轮事件肯定结束了。

#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("tcp服务器");

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

    //连接信号槽(如何处理连接)
    connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);

    //绑定并监听端口号
    if(!tcpServer->listen(QHostAddress::Any, 8080))
    {
        QMessageBox::critical(this, "服务器启动失败", tcpServer->errorString());
        return;
    }
}

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

void Widget::processConnection()
{
    //拿到socket对象
    QTcpSocket *clientSocket = tcpServer->nextPendingConnection();
    //peerAddress表示对端的ip地址
    QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] online";

    //显示到界面
    ui->listWidget->addItem(log);

    //通过信号槽处理客户端发来的请求
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
       //读取请求
        QString request = clientSocket->readAll();
       //处理请求
        const QString &response = process(request);
        //返回响应
        clientSocket->write(response.toUtf8());
        //记录日志
        QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]"
                + "req: " + request + ", resp: " + response;
        ui->listWidget->addItem(log);
    });

    //客户端断开连接
    //disconnected表示已经断开,是一个信号
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] offline";
        ui->listWidget->addItem(log);
        //手动释放clientSocket
        clientSocket->deleteLater();
    });
}

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

此段代码,从某种意义上来说,不够严谨,因为tcp是面向字节流的,可能会分为多段。

更好的做法是,将收到的数据,放到一个较大字节的缓冲区当中,然后按照约定好的协议,进行数据解析提取。

TCP回显客户端

ui界面:

image-20240925225359499

对于客户端,使用的就是QTcpSocketQTcpServer只是在服务端使用的

  • 调用connectToHost函数,此时系统就开始和对方服务器三次握手,三次握手也是需要时间的,而这个函数并不会阻塞等待握手完毕,是一个非阻塞的函数。
    所以需要搭配waitForConnected,等待连接建立成功
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置窗口标题
    this->setWindowTitle("客户端");
    //创建socket对象实例
    socket = new QTcpSocket(this);
    //和服务器建立连接
    socket->connectToHost("127.0.0.1", 8080);
    //连接信号槽
    connect(socket, &QTcpSocket::readyRead, this, [=](){
        //读取响应内容
        QString response = socket->readAll();
        //响应内容显示到界面
        ui->listWidget->addItem("server say# " + response);
    });
    //等待连接建立结果
    if(!socket->waitForConnected())
    {
        QMessageBox::critical(this, "连接服务器失败", socket->errorString());
        return;
    }
    
}

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


void Widget::on_pushButton_clicked()
{
    //获取输入框内容
    const QString &text = ui->lineEdit->text();
    //发送到服务器
    socket->write(text.toUtf8());
    //将发送的消息显示到界面
    ui->listWidget->addItem("client say#" + text);
    //清空输入框内容
    ui->lineEdit->setText("");
}

在这里插入图片描述

Linux当中写Tcp服务器的时候,如果多个客户端同时访问,就只会生效一个,然后引入线程,每个客户端一个线程;

而这里并没有出现这类情况,这是因为之前是写的两层循环,里面的循环没有结束,导致外层循环不能快速调用到accpet,导致第二个客户端无法进行处理。

引入多线程,本质上就是将双重循环,化简成两个独立的循环。

Qt里面的信号槽机制,就无需写这些循环,比较方便。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Java-IO模型

所谓I/O就是计算机内存与外部设备之间拷贝数据的过程。由于CPU访问内存的速度远远高于外部设备&#xff0c;因此CPU是先把外部设备的数据读到内存里&#xff0c;然后再进行处理。对于一个网络I/O通信过程&#xff0c;比如网络数据读取&#xff0c;会涉及两个对象&#xff0c;一…

Ubuntu 开机自启动 .py / .sh 脚本,可通过脚本启动 roslaunch/roscore等

前言 项目中要求上电自启动定位程序&#xff0c;所以摸索了一种 Ubuntu 系统下开机自启动的方法&#xff0c;开机自启动 .sh 脚本&#xff0c;加载 ROS 环境的同时启动 .py 脚本。在 . py 脚本中启动一系列 ROS 节点。 一、 .sh 脚本的编写 #!/bin/bash # gnome-terminal -- …

javaWeb,Maven

前端打包的程序放在nginx中 查看哪个程序占用了80端口号 Maven&#xff1a;

rk3399开发环境的介绍

零. 前言 由于Bluez的介绍文档有限&#xff0c;以及对Linux 系统/驱动概念、D-Bus 通信和蓝牙协议都有要求&#xff0c;加上网络上其实没有一个完整的介绍Bluez系列的文档&#xff0c;所以不管是蓝牙初学者还是蓝牙从业人员&#xff0c;都有不小的难度&#xff0c;学习曲线也相…

生产绩效考核管理的六大指标

生产绩效考核管理的六大指标 绩效考核是指生产部所有人员通过不断丰富自己的知识、提高自己的技能、改善自己的工作态度&#xff0c;努力创造良好的工作环境及工作机会&#xff0c;不断提高生产效率、提高产品质量、提高员工士气、降低成本以及保证交期和安全生产的结果和行为…

极狐GitLab 17.4 升级指南

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab https://dl.gitlab.cn/6y2wxugm 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文分享极狐GitLab 17.4 升级…

9.24作业

将昨天的My_string类中的所有能重载的运算符全部进行重载 、[] 、>、<、、>、<、! 、&#xff08;可以加等一个字符串&#xff0c;也可以加等一个字符&#xff09;、输入输出(<< 、 >>) 代码如下 MyString.h #ifndef MYSTRING_H #define MYSTRING_…

检查一个CentOS服务器的配置的常用命令

在CentOS系统中&#xff0c;查看服务器配置的常用命令非常丰富&#xff0c;这些命令可以帮助用户快速了解服务器的硬件信息、系统状态以及网络配置等。以下是一些常用的命令及其简要说明&#xff1a; 1. 查看CPU信息 (1) cat /proc/cpuinfo&#xff1a;显示CPU的详细信息&…

react+antdMobie实现消息通知页面样式

一、实现效果 二、代码 import React, { useEffect, useState } from react; import style from ./style/index.less; import { CapsuleTabs, Ellipsis, Empty, SearchBar, Tag } from antd-mobile; //消息通知页面 export default function Notification(props) {const [opti…

国家推动工业制造业数字化转型的政策与措施综述

在全球化与信息化浪潮的推动下&#xff0c;工业制造业正经历着前所未有的数字化转型。这一变革不仅深刻重塑了生产方式与商业模式&#xff0c;更成为提升国家竞争力、促进经济高质量发展的关键驱动力。随着科技的飞速发展&#xff0c;数字化转型已成为工业制造业转型升级的必然…

基于微信小程序的健康管理系统(源码+定制+文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

叉车防撞系统方案,引领安全作业新时代

在现代工业的舞台上&#xff0c;叉车如同忙碌的“搬运工”&#xff0c;在仓储和制造环境中发挥着不可或缺的作用。然而&#xff0c;随着叉车使用频率的不断攀升&#xff0c;安全事故也如影随形&#xff0c;给企业带来经济损失的同时&#xff0c;更严重威胁着操作人员的生命安全…

拓扑结构的理解

拓扑结构是数学中的一个重要概念&#xff0c;主要研究空间的性质及其在连续变换下的保持不变的特征。它是拓扑学的基础内容之一&#xff0c;广泛应用于多个领域&#xff0c;包括数学、物理、计算机科学等。 基本概念 1. 拓扑空间&#xff1a; 一个集合 X 和一个拓扑T 的组合…

毕业设计选题:基于ssm+vue+uniapp的购物系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

基于Python可视化的学习系统的设计与实现(源码+文档+调试+答疑)

文章目录 一、项目介绍二、视频展示三、开发环境四、系统展示五、代码展示六、项目文档展示七、项目总结 大家可以帮忙点赞、收藏、关注、评论啦 &#x1f447;&#x1f3fb; 一、项目介绍 随着计算机技术发展&#xff0c;计算机系统的应用已延伸到社会的各个领域&#xff0c…

真正的Open AI ——LLaMA颠覆开源大模型

1. LLaMA 简介 LLaMA&#xff08;Large Language Model Meta AI&#xff09;是由Meta&#xff08;原Facebook&#xff09;推出的一个大型语言模型系列&#xff0c;旨在通过更小的模型规模和更少的计算资源&#xff0c;实现与其他主流语言模型&#xff08;如GPT&#xff09;相媲…

Linux-TCP重传

问题描述&#xff1a; 应用系统进行切换&#xff0c;包含业务流量切换&#xff08;即TongWeb主备切换&#xff09;和MYSQL数据库主备切换。首先进行流量切换&#xff0c;然后进行数据库主备切换。切换后发现备机TongWeb上有两批次慢请求&#xff0c;第一批慢请求响应时间在133…

完成UI界面的绘制

绘制UI 接上文&#xff0c;在Order90Canvas下创建Image子物体&#xff0c;图片资源ui_fish_lv1&#xff0c;设置锚点&#xff08;CountdownPanelImg同理&#xff09;&#xff0c;命名为LvPanelImg,创建Text子物体&#xff0c;边框宽高各50&#xff0c; &#xff0c;重名为LvT…

vue结合element-ui实现列表拖拽变化位置,点击拖动图标拖动整个列表元素,使用tsx格式编写

先来看下需要实现的效果 当鼠标放在左侧图标上时&#xff0c;可以拖动整个列表元素&#xff0c;调整顺序 思路介绍 使用draggable可以设置元素可拖动&#xff0c;然后分别设置三个事件处理函数&#xff0c;监听onDragstart、onDragover、onDragend三个事件 注意&#xff1a…