(9)Qt---网络编程(半双工通信)

news2024/12/23 14:31:59

目录

1. 复习

1.1 UDP 与TCP

1.2 IP地址与端口号

2. 前期准备

3. 编程内容


1. 复习

1.1 UDP 与TCP

UDP TCP 协议相同点:都存在于传输层

  • TCP(即传输控制协议):

是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、

数据无失序、数据无重复到达的通信)

适用情况:

 1、适合于对传输质量要求较高,以及传输大量数据的通信。

 2、在需要可靠数据传输的场合,通常使用TCP协议

 3、MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

  • UDP :用户数据报协议

UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用情况:

1、发送小尺寸数据(如对DNS服务器进行IP地址查询时)

2、在接收到数据,给出应答较困难的网络中使用UDP。

3、适合于广播/组播式通信中。

4、MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议

5、流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输

1.2 IP地址与端口号

  • IP地址

基本概念:

IP地址是Internet中主机的标识,Internet中的主机要与别的机器通信必须具有一个IP地址。

  • 端口号

基本概念:

为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分。

众所周知端口:1~1023(1~255之间为众所周知端口,256~1023端口通常由UNIX系统占用)

已登记端口:1024~49151    

动态或私有端口:49152~65535

自定义程序的端口号建议:2000-65535,去除连号,例如8888

2. 前期准备

跟数据库一样,网络功能也需要在.pro文件中增加network模块。

Qt的TCP通信结构图,如下所示(原理大概下图所示,但是具体的API有所不同)。

3. 编程内容

本次要实现一个基于TCP的聊天程序,需要使用的类有:

  • QTcpServer

服务器管理类:管理服务器的多个连接,直接继承了QObject,因此不具备IO能力。

相关函数如下:

// 构造函数
QTcpServer::QTcpServer(QObject * parent = 0)
// 服务器开启监听,等待客户主动发起连接
// 参数1:监听来自于哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
// 参数2:服务器端口号
// 返回值:监听开启结果
bool QTcpServer::listen(
                const QHostAddress & address = QHostAddress::Any, 
                quint16 port = 0)
// 有新连接建立的通知信号
void QTcpServer::newConnection() [signal]
// 服务器是否还在监听
bool QTcpServer::isListening() const
// 关闭服务器
void QTcpServer::close()
// 返回一个就绪的连接对象,此对象用于跟某个客户端进行IO操作
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
  • QTcpSocket

TCP连接类:进行网络IO操作,间接继承QIODevice类。

相关函数如下:

// 构造函数
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:读写模式,默认为可读可写
void QAbstractSocket::connectToHost(const QString & hostName,
                                    quint16 port, 
                                    OpenMode openMode = ReadWrite) [virtual]
// 连接是否处于打开状态
bool QIODevice::isOpen() const
// 关闭连接
void QIODevice::close() [virtual]
// 拿到对面的IP地址封装类对象,如果没有连接返回QHostAddress::Null
QHostAddress QAbstractSocket::peerAddress() const
// 连接断开发射的信号
void QAbstractSocket::disconnected() [signal]
// 有数据可读时发射的信号
void QIODevice::readyRead() [signal]
  • QTextStream 

文本流类:是一种高效地文本数据IO的辅助类。

// 构造函数
// 参数为QIODevice的派生类对象
QTextStream::QTextStream(QIODevice * device)
// 发送字符串
// 参数必须是QString类型,注意不要使用const char*
// 返回值是当前类型的引用,表示支持链式调用,因此连续追加发送的内容
QTextStream & QTextStream::operator<<(const QString & string)
// 读取最大值为maxlen个字符的内容到返回值
QString QTextStream::read(qint64 maxlen)
// 读取最大字符数maxlen的一行字符到返回值
QString QTextStream::readLine(qint64 maxlen = 0)
// 读取所有字符到返回值
// 
QString QTextStream::readAll()

client:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QtWidgets>
//连接类
#include <QTcpSocket>
//文件流类
#include <QTextStream>
#include <QTcpServer>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QTcpSocket *client; //连接对象

private slots:
    void btnConnClickedSlot();
    void btnSendClickedSlot();
    //连接和断开的检测槽函数
    void connectedSlot();
    void diaconnectedSlot();
    void readReadSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnClickedSlot()));
    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));

    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);

    // 创建连接对象
    client = new QTcpSocket(this);
    //client = new QTcpServer(this);
    //连接状态检测的信号槽
    connect(client,SIGNAL(connected()),this,SLOT(connectedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(diaconnectedSlot()));
    connect(client,SIGNAL(readyRead()),this,SLOT(readReadSlot()));

}

Dialog::~Dialog()
{
    // 如果客户端还在连着,关掉
    if(client->isOpen()){
        client->close();
    }
    delete ui;
}
void Dialog::btnConnClickedSlot(){
    //默认输入有效,连接到服务器
    //参数1:服务器的IP地址
    //参数2:服务器的端口号
    client->connectToHost(ui->lineEditIp->text(),8887);

}

void Dialog::btnSendClickedSlot(){
    //获取用户输入的内容
     QString msg = ui->lineEditMsg->text();
     if(msg == ""){
         QMessageBox::warning(this,"提示","请输入要发送的内容!");
         return;
     }
     //创建文本流对象
     QTextStream output(client);
     //发送内容
     output << msg;
     //清空输入框
     ui->lineEditMsg->clear();
}

void Dialog::connectedSlot()
{
    //屏蔽连接按钮
    ui->pushButtonConn->setEnabled(false);
    ui->pushButtonConn->setText("已连接");
    //释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}

void Dialog::diaconnectedSlot()
{
    //恢复连接按钮
    ui->pushButtonConn->setEnabled(true);
    ui->pushButtonConn->setText("连接!");
    //屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}
void Dialog::readReadSlot(){
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    QTextStream input(client);
    QString msg = input.readAll();
    ui->textBrowser->append(msg);
}

ui界面

 

 server:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QtWidgets>
//网络相关类
#include <QTcpServer>
#include <QTcpSocket>
#include <QDateTime>
#include <QTextStream>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    // 管理类服务器对象
    QTcpServer *server;
    QTcpSocket*socket = NULL; //简单期间,只保留一个客户端连接

private slots:
    //新连接建立的槽函数
    void newConnSlot();
    //网络连接断开的槽函数
    void disconnectedSlot();
    //读取信息的槽函数
    void readReadSlot();
    void btnSendClickSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    // 设置窗口标记,始终前台显示
    setWindowFlags(Qt::WindowStaysOnTopHint);
    // 创建管理类对象
    server = new QTcpServer(this);
    //server = new QTcpSocket(this);
    //连接服务器通知的信号槽
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnSlot()));
    // 开启监听,等待客户主动发起连接
    //参数1:监听来自哪个IP地址的请求,默认值为不限制IP地址
    //QHostAddress类是ip地址的封装类
    //参数2:服务器端口号
    server->listen(QHostAddress::Any,8887);

}

Dialog::~Dialog()
{
    if(server->isListening()) // 如果在监听
            // 关闭服务器
            server->close();
    delete ui;
}


void Dialog::newConnSlot()
{
    //如果不是第一次连接,先踢掉之前的连接
    if(socket !=NULL){
        socket->close();
    }
    //拿到与客户端进行连接的QTcpSocket对象(绿蛋)
    socket = server->nextPendingConnection();
    //建立断开连接的通知的信号槽
    connect(socket,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
    //建立读取消息的信号槽
    connect(socket,SIGNAL(readyRead()),this,SLOT(readReadSlot()));

    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickSlot()));

    //拿到对面客户端的IP与端口号
    QString ip = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    //输出信息
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("新连接来了!");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");


}

void Dialog::disconnectedSlot()
{
    //拿到对面客户端的IP与端口号
    QString ip = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    //输出信息
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("老连接走了!");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}

void Dialog::readReadSlot()
{
    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    QTextStream input(socket);
    //读取数据
    QString msg = input.readAll();
    //展示
    ui->textBrowser->append(msg);
}

void Dialog::btnSendClickSlot()
{
    QString msg2 = ui->lineEditMsg->text();
    if(msg2 == ""){
        QMessageBox::warning(this,"提示","请输入要发送的内容!");
        return;
    }
    QTextStream output(socket);
    output << msg2;
    ui->lineEditMsg->clear();

}

ui界面

 

 运行结果:

 

 

 

 

 

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

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

相关文章

牛客网面试必刷:BM18 二维数组中的查找

牛客网面试必刷&#xff1a;BM18 二维数组中的查找 前言一、解法1&#xff1a;逐行使用二分搜索二、解法2&#xff1a;线性搜索&#xff08;推荐&#xff09; 前言 二分查找常见的是在一维数据中进行&#xff0c;在我的上一篇文章中已经有介绍。 一维数组查找&#xff1a;BM1…

STM32F4_DHT11数字温湿度传感器

目录 前言 1. DHT11简介 2. DHT11数据结构 3. DHT11的传输时序 3.1 DHT11开始发送数据流程 3.2 主机复位信号和DHT11响应信号 3.3 数字 “0” 信号表示方法 3.4 数字 “1” 信号表示方法 4. 硬件分析 5. 实验程序详解 5.1 main.c 5.2 DHT11.c 5.3 DHT11.h 前言 DH…

c#笔记-泛型

泛型方法 假设我们要编写一个方法&#xff0c;它可以获取任意类型数组中的最大值&#xff0c; 并返回该值。我们可能会这样写&#xff1a; static int GetMax(int[] array) {Array.Sort(array);return array[array.Length - 1]; }这个方法可以实现我们的需求&#xff0c;但是…

【虚拟机】VMware16保姆级安装教程

大家好&#xff0c;我是雷工&#xff01; 工作中需要用到各种各样的工控软件&#xff0c;有时候甚至需要不同版本的软件&#xff0c;但频繁装卸软件比较麻烦&#xff0c;而且像WinCC和博图软件对系统要求比较严格&#xff0c;卸载重装可能就出问题&#xff0c;此时就不得不重装…

MODBUS通讯程序设计流程图

MODBUS通讯一般来说是基于RS485电平的通讯&#xff0c;RS485是半双工&#xff0c;很多单片机工程师做的通讯不是稳定&#xff0c;主要原因是流程没有掌控好。 我以前也犯过和他们一样错误&#xff0c;觉得很容易。在QQ群里&#xff0c;有位老工程师和大家一样犯了同样的毛病。…

宣布!BNB Greenfield 测试网上的部署

我们很高兴地宣布&#xff0c;4EVERLAND Hosting 现在支持在 BNB Greenfield Testnet 上部署&#xff0c;为开发和托管去中心化应用程序 (dApp) 提供更具可扩展性和安全性的选项。 这种集成使项目团队能够轻松且经济地利用 4EVERLAND 在去中心化托管方面的尖端技术和专业知识&…

No module named ‘torch_geometric‘解决办法——PyG(PyTorch Geometric)安装教程

1、查询已安装的torch版本 一定要先查询自己的torch版本&#xff0c;也不同版本的依赖跟库都是不一样的。 import torch print("torch版本&#xff1a;",torch.__version__)输出结果&#xff1a; 2、安装依赖 2.1 进入官网下载依赖。 官网下载链接&#xff1a;…

word查找与替换

比如我有一个需求&#xff1a;将word中符号和中文为全角&#xff0c;但英文字符和数字为半角。 这种我们应该怎么办&#xff1f; 可以通过查找和替换解决。 按ctrl H即可看到查找替换框&#xff0c;注意word中查找替换不是ctrl F 查找 功能&#xff1a;在以下选项中查找 我们…

WiFi(Wireless Fidelity)基础(十)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

Springboot +Flowable,各种历史信息如何查询(二)

一.简介 正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中&#xff0c;执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中&#xff0c;也就是流程历史信息表。 假设有一个流程&#xff0c;流程图如下&#xff1a; 当这个流程执行完毕后&#xff0c;以 ACT_RU_ 为前缀的…

【ubuntu】alias命令

1 alias的作用 alias允许用户为命令创建简单的名称或缩写。如果需要在窗口输入复杂且较多的命令时&#xff0c;alias就可以减轻用户的记忆负担&#xff0c;发挥作用啦。 2 语法 &#xff08;1&#xff09;简单命令 alias [name”value”] 举例&#xff1a;alias ppwd 命令…

学习网络安全有哪些就业方向?网络安全就业前景!

在当下&#xff0c;网络安全是非常重要的&#xff0c;与我们的生活息息相关&#xff0c;因此网络安全课程也成为了热门技术&#xff0c;引发众多人学习。那么学习网络安全有哪些就业方向?我想很多人都不太了解&#xff0c;接下来我们一起来看看吧。 首先说一下什么是网络安全…

科罗拉多州立大学发布CSU-MLP模型,用随机森林预测中期恶劣天气

本文首发自 HyperAI超神经微信公众号~ 内容一览&#xff1a;近期&#xff0c;来自美国科罗拉多州立大学与 SPC 的相关学者联合发布了一个基于随机森林的机器学习模型 CSU-MLP&#xff0c;该模型能够对中期 (4-8天) 范围内恶劣天气进行准确预报。目前该成果刊已发表在《Weather …

进销存是什么意思?值得推荐的进销存软件有哪些?

在这个消费不断升级、直播带货和电商不断冲击的时代&#xff0c;实体店的生存变得越来越艰难&#xff0c;如何提高门店管理效率、降低管理成本、提升门店客流量&#xff0c;是实体店当下急需解决的几大问题。 进销存软件就是为了帮助实体店解决以上几大难题而设计的&#xff0c…

Xline v0.4.0: 一个用于元数据管理的分布式KV存储

Xline是什么&#xff1f;我们为什么要做Xline&#xff1f; Xline是一个基于Curp协议的&#xff0c;用于管理元数据的分布式KV存储。现有的分布式KV存储大多采用Raft共识协议&#xff0c;需要两次RTT才能完成一次请求。当部署在单个数据中心时&#xff0c;节点之间的延迟较低&a…

对STM32栈的理解Stack_Size EQU 0x00000400

对STM32栈的理解Stack_Size EQU 0x00000400 Stack_Size EQU 0x00000400表示什么意思可以通过查找flash内存的方式定位存储1.flash2.RAM内部 本人主要为个人参考网络及个人总结而来比较&#xff0c;如有雷同请告知&#xff0c;即刻删除 以下引用网上资料 理解堆和栈的区别 &…

RK3588 设备树pinctrl gpio子系统解析,解决GPIO无法正确拉高拉低的问题,RK3588设备树详解

一、RK3588设备树结构 firefly的官方说明文档RK3588gpio系统说明 function {group {rockchip,pin <bank gpio func &ref>;}; };其中&#xff0c;bank是所属的组&#xff0c;Core-3588J 有 5 组 GPIO bank&#xff1a;GPIO0-GPIO4&#xff0c;每组又以 A0-A7, B0-B…

DiffDock源码解析

DiffDock源码解析 数据预处理 数据输入方式 df pd.read_csv(args.protein_ligand_csv), 使用的是csv的方式输入&#xff0c; 格式&#xff1a; 不管受体还是配体&#xff0c; 输入可以是序列或者3维结构的文件 如果蛋白输入的是序列&#xff0c;需要计算蛋白的三维结构&am…

Type-C接口在显示器上有什么作用?Type-C 显示器方案介绍

一显示器的Type-C口&#xff0c;是未来显示器的接口的“终极形态”&#xff0c;未来显示器可以不要USB-A&#xff0c;不要HDMI&#xff0c;不要3.5音频&#xff0c;甚至不要DP口&#xff0c;但Type-C口一定会越来越多。 二显示器Type-C有什么用&#xff0c;有什么类型 1只可以…

大势智慧软硬件技术答疑第三期

1.重建大师6.0试用版&#xff0c;怎么导出DOM、DEM&#xff1f; 答&#xff1a;需要先生成三维模型&#xff0c;然后再提交产品选择DOM和DEM。 2.麻烦问下&#xff0c;修模出来贴的纹理图片&#xff0c;导出osgb后再打开就模糊了是什么情况&#xff1f; 答&#xff1a;拿高清…