QT 学习笔记(十四)

简介: QT 学习笔记(十四)

文章目录



一、TCP/IP 通信过程简介

1. Socket 通信

2. Linux 下的 TCP/IP 通信过程

3. QT 下的 TCP/IP 通信过程

3.1 在 QT 中实现 TCP/IP 服务器端通信的流程

3.2 在 QT 中实现 TCP/IP 客户端通信的流程

二、TCP/IP 通信过程操作实现

1. 服务器端

2. 客户端

三、服务器端和客户端实现代码

1. 主函数 main.c

2. 服务器端头文件 widget.h

3. 服务器端源文件 widget.cpp

4. 客户端头文件 clientwidget.h

5. 客户端源文件 clientwidget.cpp


由于每次代码都是在原有程序上修改,因此除了新建项目,不然一般会在学完后统一展示代码。

提示:具体项目创建流程和注意事项见QT 学习笔记(一)

提示:具体项目准备工作和细节讲解见QT 学习笔记(二)



一、TCP/IP 通信过程简介

1. Socket 通信


QT 中提供的所有的 Socket 类都是非阻塞的。

QT 中常用的用于 socket 通信的套接字类:

QTcpServer:用于 TCP/IP 通信,作为服务器端套接字使用。

QTcpSocket:用于 TCP/IP 通信,作为客户端套接字使用。

QUdpSocket:用于 UDP 通信,服务器,客户端均使用此套接字


2. Linux 下的 TCP/IP 通信过程

c0633b121b7d40b79574949e7f026bf7.png



3. QT 下的 TCP/IP 通信过程

ab256604fe1542aca06623639ef3ec44.png


3.1 在 QT 中实现 TCP/IP 服务器端通信的流程


1) 创建套接字。

(2) 将套接字设置为监听模式。

(3) 等待并接受客户端请求:可以通过 QTcpServer 提供的 void newConnection() 信号来检测是否有连接请求,如果有可以在对应的槽函数中调用 nextPendingConnection 函数获取到客户端的 Socket 信息(返回值为 QTcpSocket* 类型指针),通过此套接字与客户端之间进行通信。

(4) 接收或者向客户端发送数据,包括如下两种:

接收数据:使用 read() 或者 readAll() 函数。

发送数据:使用 write() 函数。


3.2 在 QT 中实现 TCP/IP 客户端通信的流程


  • (1) 创建套接字。
  • (2) 连接服务器:可以使用 QTcpSocket 类的 connectToHost() 函数来连接服务器。
  • (3) 向服务器发送或者接受数据。



二、TCP/IP 通信过程操作实现

  • 生成一个新的项目,具体步骤过程见提示。


在生成新项目的过程中,我们选择基类为 QWidget ,这是因为 QWidget 当中比较干净,不存在别的东西,而 QMainWindow 虽然也可以实现,但其中存在工具栏,菜单栏,核心控件等东西,比较复杂。


1. 服务器端


首先,我们在 ui 界面布置出服务器端的窗口界面,包含两个按钮(发送 send 和 关闭 close)和两个文本编辑区(分别用来输入和显示),具体界面布局如下图所示。

629f0bba750d48259ae89e1d4e6a4e83.png


同时,我们将上面的文本编辑区设置为 readOnly(只读)。这里需要注意的是只修改上面的文本编辑区,下面的是用来输入信息的。

13c4f4e19ab64430bac119a2149b0561.png


我们在 TCP.pro 当中加入 QT += network 代码,以便后续工作可以顺利开展。

通过 QT 提供的 QTcpServer 类实现服务器端的 socket 通信。

只有服务器端需要两个套接字,分别是监听套接字 QTcpServer 和通信套接字 QTcpSocket。

这里要使用 Lambda 表达式,需要提前在 TCP.pro 当中加入 CONFIG += C++11 代码。

我们还需要对两个按钮在 ui 界面进行转到槽函数的操作,并进行代码的编写,由于这里需要服务器端和客户端相结合才可以看到现象,故在此处不过多展示实现现象(主要在客户端展示)。

当我们完成基础代码的编写后,运行程序,直接点击 send 按钮,会产生程序异常结束的现象,具体实现现象如下图所示。


ec6b32ccfb514b68ba7144e7fa8e9172.png



  • 这是因为当我们按下按钮时 tcpsocket 并没有内容,因此,我们需要在代码当中添加 if 判断,tcpsocket 是否为空,若为空,就直接返回,不进入按钮操作。
  • 同时,当我们断开与客户端的连接后,也需要将 tcpsocket 置为空。


2. 客户端


客户端通过使用 QT 提供的 QTcpSocket 类可以方便的实现与服务器端的通信。

在完成服务器端后,我们进行客户端的编写,客户端只有一个套接字就是通信套接字 tcpsocket。

这里我们不重新开一个项目,而是将两部分放在一块。但是,服务器端和客户端的 ui 界面并不相同,因此,我们需要在添加一个 QT 设计师界面类,对客户端的 ui 界面进行设计。

8428fe524a444f8e9ef09c0c71372578.png


界面模板默认 Widget 即可。

3b681abc918a4360b320fd21d3293ff6.png


随后,我们在 ui 界面布置出客户端的窗口界面,包含两个标签(分别是服务器端口和服务器 IP )以及他们对应的行编辑区,三个按钮(分别是发送 send ,关闭 close 和连接 connect)和两个文本编辑区(分别用来输入和显示)。

同时,我们将上面的文本编辑区设置为 readOnly(只读)。这里需要注意的是只修改上面的文本编辑区,下面的是用来输入信息的。

其中的服务器端口的行编辑区直接默认值是 8888(与服务器端相对应),服务器 IP 的行编辑区直接默认值是 127.0.0.1(本地连接),具体界面布局如下图所示。

ccc94331e58d4113ab48671474354535.png



在完成 ui 界面的设计后,我们先编写按钮 connect ,并测试是否可以与服务器端连接,具体实现现象如下图所示。

e476b633430e473794d376ac88f9000e.png


但是,这里存在一个问题,就是客户端自己本身并不知道是否连接成功,对此进行优化,使客户端显示成功和服务器端建立好连接,得到如下的实现现象。

26f24a47780344709d5d90ba9f92de67.png


在确保可以完成服务器端和客户端的连接之后,我们进行信息收发功能的实现(主要包括客户端发送信息和服务器端获取客户端编辑区信息并展现),具体实现现象如下图所示。

833c5c8dded246e6a8180c095b011433.png



最后,实现断开服务器端和客户端连接的功能,就是无法继续发送信息,但是窗口仍然保存,不会关闭,可再次通信发送。


三、服务器端和客户端实现代码

1. 主函数 main.c

#include "widget.h"
#include <QApplication>
#include "clientwidget.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    CLientWidget w2;
    w2.show();
    return a.exec();
}


2. 服务器端头文件 widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>//监听套接字
#include <QTcpSocket>//通信套接字
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();
private slots:
    void on_buttonsend_clicked();
    void on_pushButton_2_clicked();
private:
    Ui::Widget *ui;
    QTcpServer *tcpserver;//监听套接字
    QTcpSocket *tcpsocket;//通信套接字
};
#endif // WIDGET_H



3. 服务器端源文件 widget.cpp

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    tcpserver = NULL;
    tcpsocket = NULL;
    //监听套接字,指定父对象,让其自动回收空间
       tcpserver = new QTcpServer(this);
       tcpserver->listen(QHostAddress::Any,8888);
       //定义窗口标题
       setWindowTitle("服务器:8888");
       connect(tcpserver,&QTcpServer::newConnection,
               [=]()
               {
                   //取出建立好连接的套接字
                   tcpsocket = tcpserver->nextPendingConnection();
                   //获取对方的IP和端口,转换为字符串便于阅读
                   QString ip = tcpsocket->peerAddress().toString();
                   qint16 port = tcpsocket->peerPort();
                   QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
                   ui->textEditread->setText(temp);
                   connect(tcpsocket,&QTcpSocket::readyRead,
                           [=]()
                           {
                               //从通信套接字中取出内容
                               QByteArray array = tcpsocket->readAll();
                               ui->textEditread->append(array);
                           }
                           );
               }
               );
}
Widget::~Widget()
{
    delete ui;
}
void Widget::on_buttonsend_clicked()
{
    if(NULL == tcpsocket)
    {
        return;
    }
    //获取编辑区内容
    QString str = ui->textEditread->toPlainText();
    //给对方发送数据,使用套接字是 tcpsocket
    tcpsocket->write(str.toUtf8().data());
}
void Widget::on_pushButton_2_clicked()
{
    if(NULL == tcpsocket)
    {
        return;
    }
    //主动和客户端断开连接
    tcpsocket->disconnectFromHost();
    tcpsocket->close();
    tcpsocket = NULL;
}



4. 客户端头文件 clientwidget.h

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include <QTcpSocket>//通信套接字
namespace Ui {
class CLientWidget;
}
class CLientWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CLientWidget(QWidget *parent = nullptr);
    ~CLientWidget();
private slots:
    void on_buttonconnect_clicked();
    void on_pushButtonsend_clicked();
    void on_pushButtonclose_clicked();
private:
    Ui::CLientWidget *ui;
    QTcpSocket *tcpsocket;//通信套接字
};
#endif // CLIENTWIDGET_H



5. 客户端源文件 clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QHostAddress>
CLientWidget::CLientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CLientWidget)
{
    ui->setupUi(this);
    setWindowTitle("客户端");
    tcpsocket = NULL;
    //分配空间,指定父对象
    tcpsocket = new QTcpSocket(this);
    connect(tcpsocket,&QTcpSocket::connected,
            [=]()
            {
                ui->textEditread->setText("成功和服务器端建立好连接");
            }
            );
    connect(tcpsocket,&QTcpSocket::readyRead,
            [=]()
            {
                //获取对方发送的内容
                QByteArray array = tcpsocket->readAll();
                //追加到编辑区中
                ui->textEditread->append(array);
            }
            );
}
CLientWidget::~CLientWidget()
{
    delete ui;
}
void CLientWidget::on_buttonconnect_clicked()
{
    //获取服务器IP和端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditport->text().toInt();
    //主动和服务器建立连接
    tcpsocket->connectToHost(QHostAddress(ip),port);
}
void CLientWidget::on_pushButtonsend_clicked()
{
    //获取编辑框内容
    QString str = ui->textEditwrite->toPlainText();
    //发送数据
    tcpsocket->write(str.toUtf8().data());
}
void CLientWidget::on_pushButtonclose_clicked()
{
    //主动和对方断开连接
    tcpsocket->disconnectFromHost();
    tcpsocket->close();
}















相关文章
|
4月前
【Qt 学习笔记】Qt窗口 | 标准对话框 | 消息对话框QMessageBox
【Qt 学习笔记】Qt窗口 | 标准对话框 | 消息对话框QMessageBox
817 4
【Qt 学习笔记】Qt窗口 | 标准对话框 | 消息对话框QMessageBox
|
4月前
|
开发者
【Qt 学习笔记】Qt系统相关 | Qt事件 | 事件的介绍及基本概念
【Qt 学习笔记】Qt系统相关 | Qt事件 | 事件的介绍及基本概念
242 4
|
4月前
【Qt 学习笔记】Qt窗口 | 标准对话框 | 文件对话框QFileDialog
【Qt 学习笔记】Qt窗口 | 标准对话框 | 文件对话框QFileDialog
904 4
|
4月前
|
数据安全/隐私保护
【Qt 学习笔记】Qt窗口 | 对话框 | 模态与非模态对话框的创建
【Qt 学习笔记】Qt窗口 | 对话框 | 模态与非模态对话框的创建
453 4
|
4月前
|
搜索推荐 C++
【Qt 学习笔记】Qt窗口 | 对话框 | 创建自定义对话框
【Qt 学习笔记】Qt窗口 | 对话框 | 创建自定义对话框
168 4
|
4月前
|
API UED
【Qt 学习笔记】Qt窗口 | 状态栏 | QStatusBar的使用及说明
【Qt 学习笔记】Qt窗口 | 状态栏 | QStatusBar的使用及说明
559 4
|
4月前
【Qt 学习笔记】Qt窗口 | 标准对话框 | 输入对话框QInputDialog
【Qt 学习笔记】Qt窗口 | 标准对话框 | 输入对话框QInputDialog
337 3
|
4月前
|
数据可视化
【Qt 学习笔记】Qt窗口 | 标准对话框 | 字体对话框QFontDialog
【Qt 学习笔记】Qt窗口 | 标准对话框 | 字体对话框QFontDialog
112 3
|
4月前
【Qt 学习笔记】Qt窗口 | 标准对话框 | 颜色对话框QColorDialog
【Qt 学习笔记】Qt窗口 | 标准对话框 | 颜色对话框QColorDialog
692 3
|
4月前
【Qt 学习笔记】Qt窗口 | 对话框 | Qt对话框的分类及介绍
【Qt 学习笔记】Qt窗口 | 对话框 | Qt对话框的分类及介绍
168 3