Qt6教程之三(13) TCP/IP通讯与socket编程

这篇具有很好参考价值的文章主要介绍了Qt6教程之三(13) TCP/IP通讯与socket编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一 前言

二 TCP/IP协议架构和通信原理

三 TCP/IP的连接与断开过程

四 Qt中开发TCP/IP原理概述

五 完整实例代码示范


一 前言

在软件开发中,常用的技术体系里面网络通信属于最重要的 “联通” 技术,是必须要掌握的技术。

那为什么网络通信如此重要呢,我想大概有以下几点:

  1. 不同硬件之间的交互,如两台电脑之间、电脑与其他硬件之间的交互;
  2. 不同软件程序之间的交互,如通过A程序通过IP地址和端口给B程序发送消息或数据;
  3. 电脑接入互联网,可以说没有网络通信技术就没有互联网;

以上是日常生活中需要使用网络的例子,另外无线通信技术、导航等底层都使用了网络通信技术,只是其底层稍有差异,根据硬件、功能、可靠性的不同而不同。

Qt框架也封装非常多的网络通信相关API供开发者使用,常用的有基于TCP/IP的Socket通信、基于UDP的Socket通信、http通信等。

本篇博客重点介绍基于TCP/IP的Socket通信,从TCP/IP基本原理出发,结合Qt的网络通信框架进行演示和讲解,最后附上完整的代码实例。

二 TCP/IP协议架构和通信原理

 TCP/IP是Transmission Control Protocol / Internet Protocol(传输控制协议/互联网络协议)的缩写。TCP和IP只是其中的2个协议,也是很重要的2个协议.

TCP/IP协议族是一组协议的集合,也叫互联网协议族,用来实现互联网上主机之间的相互通信。

 TCP/IP四层模型如下:

qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

 qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

网络接口层
    用于协作IP数据在已有网络介质上传输的协议。实际上TCP/IP标准并不定义与ISO数据链路层和物理层相对应的功能。它提供TCP/IP协议的数据结构和实际物理硬件之间的接口。 

   

网络层
    本层包含IP协议、RIP协议(Routing Information Protocol,路由信息协议),负责数据的分片、寻址和路由。同时还包含网间控制报文协议(Internet Control Message Protocol,ICMP)用来提供网络诊断信息。

   

传输层
    传输层有时候也称为主机到主机层,提供两种端到端的通信服务。

    1)TCP协议(Transmission Control Protocol)提供可靠的数据流运输服务。

    2)UDP协议(Use Datagram Protocol)提供不可靠的用户数据报服务。    

应用层
     应用层对应于OSI参考模型的应用层、表示层、会话层,为用户提供所需要的各种服务,例如:HTTP、TFTP、FTP、NFS、SMTP等。应用层是协议栈与主机上应用程序或进程接口的地方,因此,也被称为处理层。
 

三 TCP/IP的连接与断开过程

连接时三次握手

qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

第一次握手:   

    建立连接之前客户端要保证服务器已经开始监听,然后客户端选择一个随机的端口开始和服务器进行socket通信。等待服务器确认;

第二次握手:

    服务器收到该报文段后,向客户端发送确认;

第三次握手:

客户端收到服务器的确认后还要向服务器给一个确认,服务器收到客户端的确认后也进入建立连接状态。

断开时四次挥手

qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

第一次挥手: 

    客户端先发送一个连接释放的报文并停止发送数据。等待服务器的确认。

第二次分手:

服务器收到该报文段后就发出确认,

第三次分手:

客户端收到服务器的确认后,等待客户端的确认。

第四次分手 : 

    客户端收到连接释放报文段后,给服务器一个确认结束的报文段,客户端也进入关闭。

四 Qt中的TCP/IP通信使用概述

Qt中的TCP/IP通信原理图如下:

qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

 对于服务器端来说:

需要两个套接字类,其中一个负责监听(QTcpServer),另外一个负责通信(QTcpSocket),大概步骤如下:

QTcpServer对象负责监听是否有客户端连接此服务器。调用监听函数并设置服务器端的监听IP地址和端口,

 tcpserver->listen(QHostAddress::Any, 8888);

当监听到客户端连接过来时,服务器端会触发newConnection这个信号,接着创建一个槽函数并和newConnection绑定,以实现和客户端的连接之后的操作 。

tcpserver = new QTcpServer(this);
tcpserver->listen(QHostAddress::Any, 8888);
connect(tcpserver, &QTcpServer::newConnection, this, &Widget::ConnectToClient);


1 //取出建立好的套接字
2 tcpsocket = tcpserver->nextPendingConnection();
3 //获取对方的端口号和ip地址,并且显示在文本编辑框中。
4 QString ip = tcpsocket->peerAddress().toString();
5 qint16 port = tcpsocket->peerPort();
6  
7 ui->textEditRead->setText(QString("[%1:%2]连接成功").arg(ip).arg(port));

接着,服务器端便可以和客户端开始通信了:

 QString str = ui->textEditWrite->toPlainText();
tcpsocket->write(str.toUtf8().data());

对于客户端来说:

首先初始化QTcpSocket对象,然后主动和服务器端连接,

 QTcpSocket *tcpsocket=new QTcpSocket(this);

//主动和服务器进行连接: 设定和服务器端指定的IP、port
tcpsocket->connectToHost((QHostAddress)ip, port);

连接成功后,客户端会触发conected信号,我们定义一个槽函数用于与信号进行绑定来处理连接成功后的处理逻辑,

connect(tcpsocket, &QTcpSocket::connected, this, &Widget::connectToServer);
void Widget::connectToServer()
{
     ui->textEditRead->setText("成功和服务器进行连接");
}

当服务器端有发送消息给客户端时,客户端会触发readyRead信号,我们自定义一个槽函数与之绑定,进行连接后的逻辑处理,

connect(tcpsocket, &QTcpSocket::readyRead, this, &Widget::ReadInformation);
void Widget::ReadInformation()
 {
   //获取套接字中的内容
    QByteArray temp = tcpsocket->readAll();
     ui->textEditRead->append(temp);
 }

至此,关于服务器端与客户端的通信过程就说完了,其实许多复杂的通信都是基于上述的过程的,比如多个客户端连接同一个服务器端、服务器端不间断接收服务器端信息等。

五 完整实例代码示范

本例将实现客户端与服务端的消息通信,可以相互发送文字、图片、视频、文件,同时具有断开连接的功能。

服务端端代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include<QTcpServer>
#include<QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QTcpServer *tcpserver;
    QTcpSocket *tcpsocket;

private slots:
    void connectSlot();
    void readData();
    void disConnectSlot();
    void on_send_clicked();
     void StateChanged(QAbstractSocket::SocketState socketState);

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("服务器端");
    resize(800,600);

    //初始化QTcpServer对象
    tcpserver=new QTcpServer(this);
    //设置监听IP和端口,这里的any指本机上面的所有网卡地址,listen方法一经调用就会开始循环监听客户端的连接了,
    tcpserver->listen(QHostAddress::Any,8899);

    //当有客户端连接过来时,就会触发newConnect信号,我们把信号绑定到槽函数上面去,便于实现连接后的逻辑
    connect(tcpserver,SIGNAL(newConnection()),this,SLOT(connectSlot()));

    //点击按钮与客户端断开连接
    connect(ui->dis_client,SIGNAL(clicked(bool)),this,SLOT(disConnectSlot()));


    //监测连接状态变化
    //connect(tcpsocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(StateChanged(QAbstractSocket::SocketState)));

    ui->send->setEnabled(false); //程序启动禁止点击发送消息按钮
    ui->dis_client->setEnabled(false); //程序启动禁用于断开连接按钮

}

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

void MainWindow::connectSlot()
{
    //获取socket对象
    tcpsocket=tcpserver->nextPendingConnection();
     //把对方的IP地址和端口显示出来
    auto ip = tcpsocket->peerAddress().toString();
    auto port = tcpsocket->peerPort();
    ui->look_message->append("与客户端建立连接成功!IP地址为:"+ip+",端口号为:"+QString::number(port));

    connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(readData()));

    ui->send->setEnabled(true); //重新建立客户端连接后,启用点击发送消息按钮
    ui->dis_client->setEnabled(true);//重新建立客户端连接后,启用断开连接按钮

}

void MainWindow::readData()
{
    ui->look_message->append("客户端说:"+tcpsocket->readAll());
}


void MainWindow::disConnectSlot()
{
   tcpsocket->disconnectFromHost();
   tcpsocket->close();
   tcpsocket=NULL;
   ui->look_message->append("与客户端断开连接成功!");
   ui->send->setEnabled(false); //断开客户端连接后,禁止点击发送消息按钮
   ui->dis_client->setEnabled(false); //点击一次断开客户端连接后,禁用于断开连接按钮
}


void MainWindow::on_send_clicked()
{
    //获取需要发送的文本
    QString str=ui->input_message->toPlainText().trimmed();



        if(str.length()>0 ){
        tcpsocket->write(str.toUtf8().data());
        ui->look_message->append("我给客户端说:"+str);
        }else {

            ui->statusbar->showMessage("提示:不能发送空消息哦!");
        }

}

void MainWindow::StateChanged(QAbstractSocket::SocketState socketState)
{
//    QAbstractSocket::UnconnectedState  0       The socket is not connected.
//    QAbstractSocket::HostLookupState   1       The socket is performing a host name lookup.
//    QAbstractSocket::ConnectingState   2       The socket has started establishing a connection.
//    QAbstractSocket::ConnectedState    3       A connection is established.
//    QAbstractSocket::BoundState        4       The socket is bound to an address and port.
//    QAbstractSocket::ClosingState      6       The socket is about to close (data may still be waiting to be written).
//    QAbstractSocket::ListeningState    5       For internal use only.

    switch (socketState) {
    case QAbstractSocket::UnconnectedState:
        ui->statusbar->showMessage("The socket is not connected");
        break;

    case QAbstractSocket::HostLookupState:
        ui->statusbar->showMessage("The socket is performing a host name lookup");
        break;
    case QAbstractSocket::ConnectingState:
        ui->statusbar->showMessage("The socket has started establishing a connection.");
        break;
    case QAbstractSocket::ConnectedState :
        ui->statusbar->showMessage("A connection is established");
        break;
    case QAbstractSocket::BoundState :
        ui->statusbar->showMessage("The socket is bound to an address and port.");
        break;
    case QAbstractSocket::ClosingState:
        ui->statusbar->showMessage("The socket is about to close (data may still be waiting to be written).");
        break;
    case QAbstractSocket::ListeningState :
        ui->statusbar->showMessage("For internal use only");
        break;

    }
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

客户端代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void StartConnect();
    void connectSucc();
    void ReadData();
    void disConnect();
    void sendMessage();


    void on_send_message_clicked();

private:
    Ui::MainWindow *ui;
    QTcpSocket *tcpsocket;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("客户端");
    resize(800,700);

    //设置默认的IP地址和端口
    ui->ip->setText("127.0.0.1");
    ui->port->setText("8899");

  tcpsocket=new QTcpSocket(this);

  //开始连接
  connect(ui->connect_bt,SIGNAL(clicked(bool)),this,SLOT(StartConnect()));

  //绑定客户端的连接信号与槽,便于在槽函数中书写逻辑
  connect(tcpsocket,SIGNAL(connected()),this,SLOT(connectSucc()));

  //当服务器端发送消息过来时,客户端接收并出来消息
  connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(ReadData()));

  //断开连接时处理业务
  connect(ui->disconnect_bt,SIGNAL(clicked(bool)),this,SLOT(disConnect()));


  ui->send_message->setEnabled(false);
  ui->disconnect_bt->setEnabled(false);

}

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

void MainWindow::StartConnect()
{
    //主动连接服务器
   tcpsocket->connectToHost(ui->ip->text().trimmed(),ui->port->text().toInt());
   ui->display_message->append("开始连接服务器");


}

void MainWindow::connectSucc()
{
    //if(tcpsocket->state()==QAbstractSocket::HostLookupState){}
    ui->display_message->append("服务器连接成功!");
    ui->display_message->append("现在可以开始互相发送消息啦!");
    ui->connect_bt->setEnabled(false);


    ui->send_message->setEnabled(true);
    ui->disconnect_bt->setEnabled(true);


}

void MainWindow::ReadData()
{
    //获取服务器端发送过来的内容
   auto data = tcpsocket->readAll();
   ui->display_message->append("服务器端说:"+data);
}

void MainWindow::disConnect()
{
   tcpsocket->disconnectFromHost();
   tcpsocket->close();
   tcpsocket=NULL;
   ui->display_message->append("客户端与服务器端断开成功!");

   ui->connect_bt->setEnabled(true);
}




void MainWindow::on_send_message_clicked()
{
    //获取文本框的输入,开始发送消息
   QString str=ui->text_input->text().trimmed();

   if(str.isEmpty()){
       ui->statusbar->showMessage("不能发送空消息哦!");
   }else {
       tcpsocket->write(str.toUtf8().data());
       ui->display_message->append("我跟服务器端说:"+str);

   }
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

运行效果:

qt6 socket,Qt学习,程序开发,tcp/ip,qt,c++

下一篇博客:

Qt6教程之三(14) 串口通信_折腾猿王申兵的博客-CSDN博客本章主要介绍Qt的串口通信!https://blog.csdn.net/XiaoWang_csdn/article/details/129768835?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129768835%22%2C%22source%22%3A%22XiaoWang_csdn%22%7D

上一篇博客:

Qt6教程之三(12) 文件管理_折腾猿王申兵的博客-CSDN博客本篇博客主要介绍文件管理,同时实现一个模仿window文件管理的小程序,支持文件的打开浏览及读写!https://blog.csdn.net/XiaoWang_csdn/article/details/129751599文章来源地址https://www.toymoban.com/news/detail-520135.html

到了这里,关于Qt6教程之三(13) TCP/IP通讯与socket编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QT6 for android 安装教程记录(版本Qt6.5.2)

    本文记录首次安装QT for andriod的详细记录。 网上的信息和资料非常多,收集和整理以及遇到的问题也各异,对新手首次接触相关开发和部署环境并不是清晰,因此,特将相关详细配置记录。 首先,开发QT for andriod 不建议使用QT5.15的版本,因为该版本不能区分相关的CPU架构,而

    2024年02月03日
    浏览(52)
  • 【QT教程】QT6物联网应用

    QT6物联网应用 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C++扩展开发视频课程 免费QT视频课程 您可以看免费1000+个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免费看 免费QT视

    2024年04月25日
    浏览(38)
  • Qt6教程之一 Qt介绍及准备工作

    在正式开始之前,需要在自己电脑上面搭建好Qt的开发环境,本教程使用的Qt开发环境为Qt6.2 。 那话不多说,咋们开始做准备工作吧! 第一步:查看电脑硬件配置及操作系统 如果有一台较好配置的电脑,那么无疑用起来是最舒心的,推荐的最佳电脑配置: 由于Qt是为跨平台而

    2024年02月09日
    浏览(38)
  • Qt6.2教程——3.Qt信号和槽

    信号和槽是Qt中一个强大的特性,用于处理对象之间的通信。它们是一种事件处理机制,允许一个对象在某个事件发生时通知另一个对象。 定义 : 信号是一个QObject的成员函数,当某个特定事件发生时,它被自动调用。它可以与一个或多个槽关联。 声明 : 在Qt类的声明中,信号

    2024年02月10日
    浏览(52)
  • Qt6教程之二(2) Spacers

    若对控件布局不熟悉的小伙伴,请参见上一篇文章 二 Qt控件之一: 布局 Layouts 。 Qt的spacer布局控件主要用于填充两个控件之间的间隔距离,当窗口缩放时,弹簧会把控件向两端(上下、左右)进行弹起,其弹起量随窗口的变化而变化。 spacer分为vertical spacer(垂直弹簧)和

    2024年02月13日
    浏览(38)
  • Qt6 c++教程9测试&调试

    调试和测试是软件开发的重要组成部分。在本章中,你将学习如何调试 Qt 项目、不同的调试技术以及 Qt 支持的调试器。调试是发现错误或不希望出现的行为的根本原因并加以解决的过程。我们还将讨论使用Qt Test框架进行单元测试。Qt Test是基于Qt的应用程序和库的单元测试框

    2024年02月05日
    浏览(42)
  • Qt6.2教程——7.QT常用控件QTextEdit

    QTextEdit 是 Qt 的一个用于编辑和显示纯文本和富文本的控件。它是功能强大且高度灵活的,可以用于实现多种任务,如文本编辑,数据表示,以及HTML的显示和编辑等。QTextEdit 支持富文本功能,这意味着它可以显示各种字体,颜色和文字格式。同时,QTextEdit 也支持文本搜索,

    2024年02月11日
    浏览(41)
  • Qt6.2教程——5.QT常用控件QLabel

    QLabel是Qt库中一个非常基础且重要的类。它主要用于在图形用户界面(GUI)中展示文本或图片。最常见的用法就是在窗口上显示一段文字或者标签,比如“用户名”,“密码”等等。QLabel继承自QFrame,因此它也可以具有框架。它能处理富文本格式,这意味着您可以改变部分文本的

    2024年02月10日
    浏览(44)
  • Qt6 c++教程2 Qt Creator简介

    Qt Creator是Qt自带的集成开发环境(IDE),用于跨平台应用程序开发。在本章中,您将学习Qt Creator集成开发环境的基础知识,并了解集成开发环境的用户界面 (UI)。我们还将了解如何在 Qt Creator中创建和管理项目。本Qt 模块包括使用Qt Creator开发一个简单的Qt应用程序、快捷方式

    2024年02月05日
    浏览(44)
  • Qt6入门教程 6:Qt元对象系统

    目录 一.什么是Qt元对象系统? 二.编译时Qt Creator偷摸做了哪些事情? 1.uic 2.rcc 3.moc Qt中的元对象系统(Meta-Object System)提供了对象间通信的信号和槽机制、运行时类型信息和动态属性系统。元对象系统是基于以下3个条件的: ●该类必须继承自QObject类; ●必须在类的私有声

    2024年01月18日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包