用QT实现客户端与服务器端通信

一、TCP\IP协议族简单介绍

可以参考一下几篇博客:

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

QT 之TCP网络编程(非常值得看的一篇博客!)_ubuntu20 tcp网络编程qt-CSDN博客

《QT编写TCP的网络编程》_qt tcp编程-CSDN博客

TCP:(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
相比而言UDP,就是开放式、无连接、不可靠的传输层通信协议。 

二、QT实现服务器端 

2.1 服务器的UI界面

2.2 服务器端编程 

2.2.1  在 .pro工程文件中的处理

QT提供了QTcpSocket类,该类属于network模块,而qt的工程文件中未加入此模块,所以需手动加入此模块。

在 .pro文件中 添加 QT += network ,其余部分不用动。

做完上面这一步后,记得要构建。 

2.2.2  在  .h头文件中的相关处理

1、添加相关头文件

2、声明相关对象

 相关代码注释:

QList<QTcpSocket*>tcpClient;   //声明一个链表,存储的是指向 QTcpSocket 对象的指针,用于存储多个 TCP 客户端的套接字指针,方便在网络编程中管理和操作多个客户端连接。 

2.2.3  在自己创建的类的  .cpp文件中的相关处理

在下面红框中的源文件中编写代码 

1、先在构造函数中初始化tcpServer对象(服务器端对象) ,选this(即当前对象)做父对象,就不需要自己去析构了tcpServer对象了。

2.通过右击按钮, 点 “转到槽”(生成相应按钮的槽函数) , 在选择clicked()信号,就可以在点击按钮时,触发槽函数( 函数里面的代码需自己写,用于实现自己想要的现象),如下图所示:


相关代码解析(大致流程):

 1、监听按钮:

       a.获取自己输入端口号。

       b.打开tcp服务器端的监听传入IP地址信息和端口号。ps: 监听地址中的客户端套接字的QHostAddress和端口号,若不自己指定,则为有线网卡和无线网卡一起监听,端口号自动分配。

       c.在点击一次按钮后,将监听按钮设为禁用状态(避免反复连接监听)。将断开按钮设置为允许使用状态。

 1、连接  连接信号(QTcpServer::newConnection) 与 槽函数:

       当客户端的连接请求到达时,服务器端会发出连接信号(newConnection),使用connect()将连接信号和槽函数连接,当连接信号发出后,就会自动执行对应的槽函数。

ps:此处使用了lambda表达式,可自行去查资料。

        a.提取客户端连接请求调用QTcpServer类的nextPendingConnection()函数。

        b.在UI界面中显示连接成功的信息。

        c.将tcpSocket添加到存储QTcpSocket*类型的链表中。

这段代码的主要作用是将连接的客户端的 IP 地址和端口号格式化为  IP:端口  的形式,并
  将其添加到下拉框中,以便用户可以选择。  


2、发送消息按钮:

        a. 获取自己在发送窗口中输入的信息

        b. 将服务端要发送的数据写入到对应的客户端,调用QTcpSocket的write()函数。

        c. 将服务端发送到客户端的数据在服务端的接收窗口中显示调用append()函数。

        d. 最后将服务端发送数据框中的内容清除。调用clear()函数。

2.接收数据(连接 QTcpSocket::readyRead信号 和 槽函数):        

        a.调用QTcpSocket类中的readAll()函数。当有数据来时,触发了readyRead信号,就将数据全部都出来。

        b.将客户端发送的数据在UI界面中展示出来。


 3、断开按钮:

        a.关闭套接字的连接调用QTcpSocket类的disconnectFromHost()函数。

        b.将客户端从列表中移除

        c.删除客户端对象,调用deleteLater()函数。

        d.关闭服务端的监听调用QTcpServer的close()函数。

        e.将监听按钮设置为允许使用状态,将断开按钮设置为禁止使用。

3.断开连接(连接 QTcpSocket::disconnected信号 和 槽函数): 

        a.在UI界面中显示断开连接成功的信息。


4、清除按钮:

        a.调用相关UI控件的ckear()函数。清除接收窗口中的数据。


2.2.4 运行结果 

2.3 相关函数的意思

2.3.1 关于split()函数:

 1、tr:这是一个用于国际化的函数,通常在 Qt 框架中使用。它的主要作用是将字符串标记为可翻译的,以便在不同语言环境中进行翻译。    "%1:%2" 是一个格式化字符串,其中 %1 和 %2 是占位符,后面会用实际的值替换它们。       
2、arg 是一个用于替换占位符的函数。arg(m_t->peerAddress().toString().split("::ffff:")[1]):获取客户端的 IP 地址,并将其转换为字符串,
3、然后通过 split 方法分割字符串,提取出 IPv4 地址部分。split("::ffff:"):这个方法将字符串分割成一个数组,分隔符是 "::ffff:"。例如,如果IP 地址是 "::ffff:192.168.1.1",使用 split 后会得到一个数组 ["", "192.168.1.1"]。
4、[1]:这是数组的索引,表示取分割后数组的第二个元素。在这个例子中,[1] 代表提取出
 "192.168.1.1",即去掉了 IPv6 地址中的映射部分。split("::ffff:"):这是一个字符串方法,用于将字符串分割成数组。这里的目的是将IPv6 地址中的 IPv4 映射地址部分(::ffff:)去掉,只保留实际的 IPv4 地址。       
5、 这段代码的主要作用是将连接的客户端的 IP 地址和端口号格式化为 IP:端口 的形式,并将其添加到下拉框中,以便用户可以选择。   

2.3.2 关于readyRead()信号:

1. 当tcpSocket准备好读取数据时,会发出readyRead信号

2. readyRead 信号表示这个 TCP 套接字有数据可读

3. 当套接字接收到来自服务器(或客户端)的数据时,这个信号会被触发。此时,可以通过调用 tcpSocket->readAll() 或其他读取函数来读取可用的数据.

4. 接收到数据:当 TCP 套接字(QTcpSocket)从远程端(例如服务器或客户端)接收到了数据时。如果对方发送了数据包,且数据已经可以被读取,

5. readyRead() 信号就会被发出。数据缓冲区可读:如果TCP连接的接收缓冲区中有数据可读,无论数据的大小,都会触发这个信号。  

三、QT实现客户端

3.1 客户端的UI界面

3.2 客户端编程

3.2.1  在 .pro工程文件中的处理

注意:这一步与上述服务器端的处理一样,下面有些地方就省略了。请看 2.2.1

3.2.2  在  .h头文件中的相关处理

 

3.2.3  在自己创建的类的  .cpp文件中的相关处理

1、连接按钮:

a. 获取 IP地址 和 port端口号。

b. 客户端尝试与指定的主机建立 TCP 连接。 调用QTcpSocket的connectToHost()函数。

c. 将相关按钮设置为 禁用 或者 允许使用。

1、连接 连接信号 和 槽函数  ( QTcpSocket::connect() ):

 a. 在UI界面中显示连接成功。

 


2、 发送按钮:

a. 获取客户端要发送的信息。

b. 将要发送的数据,写入到套接字,传给服务器。 调用QTcpSocket类的write()函数。

c. 将发送信息框的内容清空。  

 

2、 连接 准备读信号 和 槽函数(QTcpSocket::readyRead信号):

a. 将传到客户端的数据都读出来。 调用QTcpSocket类的 readAll() 函数。

b. 将传来的数据在UI界面的接收框中显示。


3、断开按钮:

 a. 断开与对方主机的连接。调用QTcpSocket类的 disconnectFromHost() 函数。

 b. 将相关的按钮状态设置为 禁用 或者 允许使用。

 

 3、连接 断开连接信号 和 槽函数(QTcpSocket::disconnected) :

a. 在UI界面显示断开连接。

 


4、清空按钮:

a. 将接收框中的数据全部清空。 

3.2.4 运行结果

//客户端的 .cpp文件
#include "tcpclientwindow.h"
#include "ui_tcpclientwindow.h"

TcpClientWindow::TcpClientWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::TcpClientWindow)
{
    ui->setupUi(this);
    tcpSocket = new QTcpSocket(this);

    //与服务器连接成功
    connect(tcpSocket, &QTcpSocket::connected, [=](){
       ui->receice_TextEdit->append("-----客户端与服务器连接成功-----");
    });

    //接收数据
    connect(tcpSocket, &QTcpSocket::readyRead, [=](){
        QByteArray data = tcpSocket->readAll();
        QString dateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");  //获取当前时间
        QString text = QString("服务器fa:  [%1]:%2").arg(dateTime).arg(QString(data));
        ui->receice_TextEdit->append(text);
    });

    //断开连接
    connect(tcpSocket, &QTcpSocket::disconnected, [=](){
        ui->receice_TextEdit->append("-----与服务器断开连接-----");
    });
}

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


void TcpClientWindow::on_connect_PushButton_clicked()  //连接服务器
{
    QString IP = ui->IP_LineEdit->text();
    QString port = ui->port_LineEdit->text();
    if(IP.isEmpty() | port.isEmpty())
    {
        return;
    }
  tcpSocket->connectToHost(IP, port.toUShort());
  ui->connect_PushButton->setDisabled(true);
  ui->disconnect_PushButton->setDisabled(false);
}

void TcpClientWindow::on_send_PushButton_clicked()  //发送数据
{
    QString message = ui->send_TextEdit->toPlainText();
    QByteArray sendMessage = message.toUtf8();
    tcpSocket->write(sendMessage);
    ui->receice_TextEdit->append("客户端:" + sendMessage);
    ui->send_TextEdit->clear();
    return;
}

void TcpClientWindow::on_disconnect_PushButton_clicked() //断开连接
{
    tcpSocket->disconnectFromHost();  //优雅地断开与对方主机的连接
    ui->connect_PushButton->setDisabled(false);
    ui->disconnect_PushButton->setDisabled(true);
}

void TcpClientWindow::on_clear_PushButton_clicked()  //清空接收框
{
    ui->receice_TextEdit->clear();
}

四、客户端与服务器端通信

 注意:端口号 要一致。

 

五、总结

本文中 服务器端 写的有点乱,有时间的话会重写。以上是我的学习笔记,如有错误,请指正,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值