QT中进行TCP/IP网络通信服务器的基础配置与简单理解

avatar
作者
猴君
阅读量:1

最近在学QT,虽然只是想入门,达到最简单的使用效果,以下是我一天的学习QT中TCP网络通讯如何进行基础配置与TCP网络通讯的简单理解。看配置的话建议直接跳转到“快速入门及配置”。

(2024.06.03添加)最近看了一篇非常详细讲TCP/IP服务(非常详细地讲了原理,三次握手、四次挥手等),这里建议看一下:https://www.cnblogs.com/crazymakercircle/p/14499211.html

目录:

1 TCP/IP协议族简单介绍与理解

1.1 摘抄自他人对TCP/IP协议族的理解与讲解

1.2 个人对TCP/IP协议族的浅显理解

2 在QT中配置TCP服务

2.1 整体思路

2.2 快速入门及配置

2.3 创建、应用TCP服务器服务

2.4 创建、应用TCP客户端端服务

3 可能遇到的问题


正文:

1 TCP/IP协议族简单介绍与理解

1.1 摘抄自他人对TCP/IP协议族的理解与讲解

我看了两篇文章

第一篇文章:https://zhuanlan.zhihu.com/p/33889997
第二篇文章https://blog.csdn.net/Jacky_Feng/article/details/114977261

看了两位作者对TCP/IP协议族的理解,以下是他们对TCP的介绍,我就直接复制粘贴过来了,感兴趣的建议直接去看原文章。
TCP通讯的定义:TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复 用。通过面向连接、端到端和可靠的数据包发送。
作用:TCP/IP 是用于因特网 (Internet) 的通信协议。 TCP/IP 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信。
优点: 1.TCP(Transimision Control Protocal)
2.传输控制协议
3.可靠的、面向连接的协议
4.传输效率低
5.UDP(User Datagram Protocal)
6.用户数据报协议
7.不可靠的、无连接的服务
8.传输效率高

1.2 个人对TCP/IP协议族的浅显理解

我基于对两位作者文章的理解,提出我对对TCP/IP协议族通讯过程的浅显理解:TCP指的不是单个具体的协议,而是一个庞大的协议族,是很多通讯协议的大集合。
通讯协议需要遵守ISO(国际标准化组织)制定的七大体系结构,但我们所用的TCP属于传输层的东西,传统上来说TCP/IP被认为是一个四层协议。协议的分层可见下图。以下我举了一个我自己理解的简单供读者参考。
图1
就像我们要给别人发一条信息,我们可以选择写信、发邮件、用手机发信息(对应通讯协议),那我们选择使用手机发信息,然而用手机发信息又可以选择的应用有很多,短信、电话、QQ、微信等等(对应应用层),最终我们选择了微信,我们给A朋友发了一则消息“Hello World”,这条消息会先通过相关协议(TCP协议)封装成机代码格式(很不专业的叫法,理解下),通过网络协议(IP)进行再封装,封装成符合网络传输的代码格式,最终由手机的网卡(数据链路层)通过WIFI、运营商网络等(物理层)发送给A朋友。A朋友则会经历一番逆操作,便可以在手机上看到我们发送的“Hello World”。(这一流程,我表述的可能很不准确甚至有误,欢迎大家私信我)我将这一过程整理成流程图,可见以下流程图。图2
跟据以上例子,应该可以简单理解这一通讯过程了,本文主要去简单配置、使用TCP/IP协议,所以我们只用关系这两个地方即可,其他数据链路层、物理层其实都属于硬件部分,这部分我们不做讨论。

2 在QT中配置TCP服务

2.1 整体思路以及QT中用到的一些函数

在TCP通讯中,TCP通讯逻辑往往遵守以下流程图。也就是说,我们完全可以跟着这张图进行编程。图上更是标明了什么时候用什么函数,绑定QT的什么信号。在这里插入图片描述

端口号的取值范围一般是0~65535,在计算机中,1024以下的一般都是系统服务的各种端口,这部分端口一般情况下不要去使用。在同一计算机中,端口使用不要重复,也就是每一个连接要对应一个端口号。本文使用的端口号是9090。

2.2 快速入门及配置

在QT中,它自带了TCP服务,无需我们再多下载其他东西。提到TCP服务,俩设备通讯肯定是绕不开的基础,那么我们可以创建一个简单的对话框,来辅助我们进行调试。
我们先做一个服务器端的网络通讯。为了方便我们观察是否通讯成功,我们可以使用QT拉一个简单的页面,页面如下图:在这里插入图片描述

为了方便对代码的理解,我这里将我给这些组件的命名标注(红字)如下图:
在这里插入图片描述

2.3 创建、应用TCP服务器服务

本文直接在本地模拟了TCP服务器通讯,所以不用设置通讯地址,只用配置端口即可。
本文要实现的目标是能将项目配置成TCP服务器,然后通过网络调试软件充当客户端,两者进行通讯,服务器与客户端通讯的消息会被记录在历史信息中,服务器向客户端发送的消息可以通过“发送消息”文本框进行输入,并通过“发送消息”按钮进行发送。
所以综上,我们只需要配置TCP服务器的端口号便可达到TCP服务器的基础功能。
我这里使用的是QT的qmake系统模板(Build system),5.14.2 32-bit的版本。在QT中,我们要使用TCP服务,以下步骤是用来制作TCP服务器的:
本文整个步骤都是按照“整体思路以及QT中用到的一些函数”中的流程图(以下简称为流程图)来写的。
1.将网络模块添加进项目,在项目中的.pro文件中第一行,添加上network代码(这一步是死的步骤),如下图:在这里插入图片描述
这一步是将QT的网络模块添入项目中,这个网络模块是QT自带的东西,QT只是在创建项目时没有引入这个模块而已,也可以简单理解为给整个项目引入网络服务包的一个操作。
2.在mainwindow.h文件中引入QTcp服务,并声明QTcpServer、QTcpSocket两个对象,QTcpServer用以配置QTcpSocket对象,这一步也就对应着流程图中的socket()这一步,过程如下图:在这里插入图片描述

这里声明的这两个对象,目的是在整个项目进行使用,根据自己的情况,也可以将它们声明在自己需要的地方(但我感觉一般都会是全局更方便吧)。
3.实例化m_s,并在m_s中设置端口与IP地址。
在这里插入图片描述
这里第一步、第三步、第四步是必要的,第二步属于一个槽函数的设置,第五步属于一个UI的设置。第一、三步对应流程图的bind()步骤设计,因为我们要监听所有IP地址,所以我们不用对IP地址进行设计,只需要设置端口就好,也就是只获取了端口号,让其转换成了符合listen()函数的16位无符号数字。第四步也就是设置了lister()步骤。
4.到此我们完成了accept()之前的所有设置,接下来就是调用将信号QTcpServer::newConnection与包含nextPendingConnection()的槽函数绑定在一起,翻译过来就是当有新设备连接时,就调用nextPendingConnection()函数,获取客户端的QTcpSocket。步骤如下:
在这里插入图片描述
这里的第一步是完成了accept(),然后第二步是完成了socket()。第三步属于一个UI优化。
5.接下来就是绑定read()与write()了,read()对应一个QTcpSocket::newConnetcion信号和readAll()函数,writer()对应了一个发送按钮的槽函数,也就是说当按钮被按下时,就会触发write()函数,进行发送信息,步骤如下:在这里插入图片描述
到此其实已经能与客户端进行正常通讯了,也就是说服务器端的基础功能其实快配置完了,现在就差最后一步close了。
6.配置close()其实最简单了,就是当客户端与服务器断开连接时会产生一个QTcpSocket::disconnected信号,我们只需要在该信号被触发时,去关闭m_t即可。步骤如下:
在这里插入图片描述
到此,QT中TCP服务器端已经配置完毕,我们可以通过网络调试助手(我在这个网址下载的网络调试助手:https://soft.3dmgame.com/down/213757.html)进行调试,试试看是否成功。效果如下:
在这里插入图片描述
附上服务器端源码:
.pro文件中就添加了一句,这里就不展示了。
mainwindow.cpp文件源码:
代码中有很多被注释掉的代码,那些代码与配置TCP服务器端无关,它们只是一些优化UI的代码,然后我这些代码引入了我自己的一个素材包,所以你取消这些被注释的代码可能会导致你的代码报错。

#include "mainwindow.h"   #include "ui_mainwindow.h"   #include "QLabel"      MainWindow::MainWindow(QWidget *parent)       : QMainWindow(parent)       , ui(new Ui::MainWindow)   {       ui->setupUi(this);          //用以设置图标   //    m_status = new QLabel;       //设置图标   //    ui->statusbar->addWidget(new QLabel("连接状态为:"));//在状态栏中添加一个QLabel标签组件   //    m_status->setPixmap(QPixmap(":/new/prefix1/cuowu.png").scaled(15,15));//设置连接失败图片   //    ui->statusbar->addWidget(m_status);          m_s = new QTcpServer(this); //实例化m_s对象       ui->COM_lineEdit->setText("9090");          connect(m_s,&QTcpServer::newConnection,this,[=](){           //检查是否连接成功           m_t = m_s->nextPendingConnection();      //        m_status->setPixmap(QPixmap(":/new/prefix1/duigou.png").scaled(15,15));//设置连接失败图片   //        ui->statusbar->addWidget(m_status);//设置连接成功图片              ui->COM_record->append("-----客户端完成与服务器的连接-----");           //接收数据           connect(m_t,&QTcpSocket::readyRead,this,[=](){               QByteArray buf = m_t->readAll();               ui->COM_record->append("客户端:" + buf);   //将数据展示出来           });              connect(m_t,&QTcpSocket::disconnected,this,[=](){   //关闭监听      //            m_status->setPixmap(QPixmap(":/new/prefix1/cuowu.png").scaled(15,15));//设置连接失败图片   //            ui->statusbar->addWidget(m_status);                  ui->COM_record->append("-----与服务器断开连接-----");               m_t->close();               m_t->deleteLater();           });       });      }      MainWindow::~MainWindow()   {       delete ui;   }         void MainWindow::on_COM_pushButton_clicked()   {       unsigned short port = ui->COM_lineEdit->text().toUShort();  //转成16位的无符号数       m_s->listen(QHostAddress::Any,port);    //打开TCP服务器端的监听       ui->COM_pushButton->setDisabled(true);  //将监听按钮设置为禁用   }      void MainWindow::on_sendMessage_pushButton_clicked()   {       QString message = ui->sendMessage_record->toPlainText();//获取将要发送的文本       QByteArray sendMessage = message.toUtf8();//将文本数据转换为UTF-8       m_t->write(sendMessage);//发送数据       ui->COM_record->append("服务器:" + sendMessage);//将发送数据记录下来       ui->sendMessage_record->clear();//将发送数据框清空   }   

2.4 创建、应用TCP客户端端服务

创建客户端的UI界面如下图所示:在这里插入图片描述

这里其实与上面的服务器端大同小异,只要能看懂服务器端,那看懂客户端那就是很简单的,这里我直接附上源码吧。
mainwindow.cpp文件:

#include "mainwindow.h" #include "ui_mainwindow.h" #include "QLabel"  MainWindow::MainWindow(QWidget *parent)     : QMainWindow(parent)     , ui(new Ui::MainWindow) {     ui->setupUi(this);      //用以设置图标 //    m_status = new QLabel;     //设置图标 //    ui->statusbar->addWidget(new QLabel("连接状态为:"));//在状态栏中添加一个QLabel标签组件 //    m_status->setPixmap(QPixmap(":/new/prefix1/cuowu.png").scaled(15,15));//设置连接失败图片 //    ui->statusbar->addWidget(m_status);      m_socket = new QTcpSocket(this);     ui->COM_lineEdit->setText("9090");      //与服务器连接成功     connect(m_socket,&QTcpSocket::connected,this,[=](){ //当成功连接时会触发这个信号 //        m_status->setPixmap(QPixmap(":/new/prefix1/duigou.png").scaled(15,15));//设置连接失败图片 //        ui->statusbar->addWidget(m_status);//设置连接成功图片          ui->COM_record->append("-----客户端完成与服务器的连接-----");     });      //接收数据     connect(m_socket,&QTcpSocket::readyRead,this,[=](){ //当有新数据时,会触发这个信号         QByteArray buf = m_socket->readAll();         ui->COM_record->append("服务器:" + buf);   //将数据展示出来     });      //断开连接     connect(m_socket,&QTcpSocket::disconnected,this,[=](){   //当与服务器断开时出发这个信号         ui->COM_record->append("-----与服务器断开连接-----");         ui->IP_pushButton->setEnabled(false);         ui->COM_pushButton->setEnabled(true);           //设置状态栏图标 //        m_status->setPixmap(QPixmap(":/new/prefix1/cuowu.png").scaled(15,15));//设置连接失败图片 //        ui->statusbar->addWidget(m_status);     }); }  MainWindow::~MainWindow() {     delete ui; }   void MainWindow::on_COM_pushButton_clicked() {     QString ip = ui->IP_lineEdit->text();     unsigned short port = ui->COM_lineEdit->text().toUShort();  //转成16位的无符号数     //连接服务器     m_socket->connectToHost(QHostAddress(ip),port);      ui->COM_pushButton->setDisabled(true);  //将监听按钮设置为禁用     ui->IP_pushButton->setDisabled(false); }  void MainWindow::on_sendMessage_pushButton_clicked() {     QString message = ui->sendMessage_record->toPlainText();//获取将要发送的文本     QByteArray sendMessage = message.toUtf8();//将文本数据转换为UTF-8     m_socket->write(sendMessage);//发送数据     ui->COM_record->append("服务器:" + sendMessage);//将发送数据记录下来     ui->sendMessage_record->clear();//将发送数据框清空 }  void MainWindow::on_IP_pushButton_clicked() {     m_socket->close();     ui->COM_pushButton->setEnabled(true);     ui->IP_pushButton->setEnabled(false); }  

到此QT中对TCP客户端、服务器端的配置已经完成,并能实现基础功能。

3 可能遇到的问题

1.我的客户端和服务器端处于同一个IP、端口中为什么连接不上?
检查一下端口号是否大于1024,我建议直接用9000以上的;IP可以尝试使用127.0.0.1,也就是本地IP,有的时候使用198.XXX这个IP可能会被电脑的防火墙阻止连接。

4 更新日志

这里其实不重要,但我有点强迫症,想要记录一下修改的地方。
2024-04-30:修改了2.1中的流程图,完善了TCP客户端的部分(原本只有服务器端的详细介绍)。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!