Qt基础知识记录(第八篇)

写在前面

博主要做的项目涉及到服务器和客户端,所以要学习一下如何用Qt搭建一个TCP客户端和一个TCP服务器,并实现它们的连接,以及客户端向服务器输出内容

工作流程

先创建服务器,再创建客户端,再实现两者的连接,再实现信息的传递

服务器的搭建

(1)新建工程

还是老一套,起名字叫TcpServer

(2)设计界面

服务器对界面的需求很低,几乎没什么需求所以就简单画一下即可

显示客户端的地址和端口,然后下边的文本框用于接收客户端传来的信息

接着修改变量命名

(3)代码编写

这里有一个至关重要的地方:Qt涉及到网络和数据库的时候,一定要在工程文件里标注

如图,最上边一行

再添加两个必要的头文件,和一个端口号的宏定义

#include <QTcpServer>
#include <QTcpSocket>

#define PORT 8000

注意:这两个头文件在保存上工程文件之后才会显示,要不然Qt里就没有这两个头文件

接着在类里声明服务器对象,并在构造函数里初始化

QTcpServer *server;
 server = new QTcpServer;

这样server就放在了栈上,然后把它在析构函数里释放,这一步代码就不附了

监听步骤:

server->listen(QHostAddress::AnyIPv4,PORT);

调用服务器类的成员函数listen,因为一台电脑大多只有一个网卡,所以第一个参数是调用开发者规定好的宏,表示随便一个地址,第二个参数是端口号

连接步骤:

 connect(server,&QTcpServer::newConnection,this,&Widget::newClientHandler);

先用connect函数连接信号与槽,然后再详细写槽函数

void Widget::newClientHandler()
{
    //建立Tcp连接
    QTcpSocket *socket = server->nextPendingConnection();
    socket->peerAddress(); //获取客户端地址
    socket->peerPort();    //获取客户端端口号
    
    ui->ipLineEdit->setText(socket->peerAddress().toString());
    ui->portLineEdit->setText(QString::number(socket->peerPort()));
    
}

我来按照我自己的理解解释一下这段代码:

声明一个开发者预先规定的对象socker,它的类型是预先规定的连接。这个连接有两个成员函数,可以获取客户端地址和客户端端口号,然后调用ui的成员函数再把它们输出就可以了。但是一定要记住修改成setText传参的类型

这样服务器就算做好了,再做一下客户端

客户端的搭建

(1)新建工程

保留服务器的项目方便后续测试,再新开一个客户端项目

命名为TcpClient,注意这个工程也要在工程文件里加上network

(2)设计界面

修改命名之后就可以写代码了。标签在后续代码也用不到,所以改不改命名都无所谓

(3)代码编写

按钮类的槽函数是最好写的,不需要connect连接

void Widget::on_cancelButton_clicked()
{
    this->close();
}

右键取消按钮转到槽,然后在槽函数里关闭窗口即可

下面实现连接的槽函数

#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>

先包含这三个必要的头文件

 QTcpSocket *socket;

在构造函数里初始化这个并在析构函数里释放

槽函数:

void Widget::on_connectButton_clicked()
{
    //获取ip地址和端口号
    QString IP=ui->ipLineEdit->text();
    QString port=ui->portLineEdit->text();
    
    //连接服务器
    socket->connectToHost(QHostAddress(IP),port.toShort());
    
    //连接服务器成功:socket对象发出信号
    connect(socket,&QTcpSocket::connected,[this]()
     {
        QMessageBox::information(this,"连接提示","连接服务器成功");
    });
    
    //连接断开,socket也会发出信号
    connect(socket,&QTcpSocket::disconnected,[this]()
    {
        QMessageBox::warning(this,"连接提示","连接异常,网络断开");
    });
}

按照我自己的理解来解释一下这段代码:

先用text()这个成员函数返回文本框的内容,然后得到端口号和地址。再用socket预先规定好的成员函数连接IP地址和端口号。再用QMessageBOx这个类的功能发出提醒。

现在客户端也搭建好了,马上就可以测试了

连接的测试

同时启动服务器和客户端的项目

然后在客户端里输入服务器的地址,客户端和服务器在一台电脑,直接输入127.0.0.1就行,端口号是事先规定的8000

结果非常好,连接成功了,接下来实现信息的传输

信息传输的实现

(1)客户端新建发送消息界面

因为客户端界面已经占满了,没有地方再发送消息,所以就新建一个界面来发送

                      

右键选择添加新文件,然后选Qt类中图标是ui/h的那一个选项

记住修改变量名

(2)连接成功后启动新界面

 connect(socket,&QTcpSocket::connected,[this]()
     {
        QMessageBox::information(this,"连接提示","连接服务器成功");

        this->hide();
        Chat *c=new Chat(socket);
        c->show();
    });

在这段代码里加入一段:将当前窗口隐藏,在堆上开一个Chat型对象,再调用这个对象的show函数展示出来。因为这里也要有socket连接,所以需要把socket传进构造函数里。在Chat类里的构造函数对应修改即可 

测试出来是可以正常跳出新窗口的

(3)实现按钮功能

实现新窗口的取消按钮

void Chat::on_clearButton_clicked()
{
    ui->textEdit->clear();
}

直接调用清空函数就行

实现新窗口的发送按钮

void Chat::on_sendButton_clicked()
{
    QByteArray ba;
    ba.append(ui->textEdit->toPlainText().toUtf8());
    socket->write(ba);
}

因为socket要操作QByteArray类型,所以需要把从textEdit里获取的内容附在QByteArray对象后边

(4)实现服务器的接收

//服务器收到客户端发送的信息,socket发出readyread的信号
    connect(socket,&QTcpSocket::readyRead,this,&Widget::clientInfoSlot);

在建立服务器和客户端连接的函数之后写一个这个。表示:服务器接收到socket发出的读取信号,然后调用clientInfoSlot函数处理

void Widget::clientInfoSlot()
{
    //获取信号的发出者
    QTcpSocket *s=(QTcpSocket *)sender();
    ui->textEdit->setText(QString(s->readAll()));
}

对这段代码的解释:

sender函数的作用是获取调用当前槽函数的信号的发出者。因为socket是一个局部变量,在这个槽函数不能直接调用,所以要写这么一句,s就是socket了。然后用textEdit的成员函数输出内容。内容就是socket里的所有内容,要记住把QByteArray类型转换成QString类型

信息传输的测试

非常好,已经成功了。

篇末总结

        Qt基础知识记录系列已经快接近尾声了,博主从最初的Qt小白到现在,已经对Qt有了一些浅显的理解。今天早上还看到博主进入了CSDN新人榜24和西安榜11,非常感动。写博客是一个很好的事情,它可以催促我不断学习,还有提升表达能力,以及测试我对知识的掌握程度。

        这一篇应该是博主更新篇幅最大的Qt项目了,博主只是一个大一下的学生,很多知识没有学,在b站上跟着大佬一步一步做的小项目。对它的原理了解的并不清楚。这个项目对博主来说也是一个很大的挑战。最后项目跑通了,博主内心也是非常高兴的。

再说一下博主更了这么多篇博客之后对Qt的理解吧:

        Qt作为一个框架,可以说所见即所得,想要实现的功能开发者早就为你考虑好了,你直接用就可以,是一个非常方便的工具。我认为使用Qt最重要的就是熟练调用各种库函数,而实际上自己真正需要实现的也没有多少。还有各种变量类型的转换,可能某一个函数要传的参数对类型有要求,这个时候就需要用到类型转换函数。

        博主对程序员的理解也逐步深入,刚开始博主以为什么东西都需要程序员自己实现,其实并不是这样,熟练运用工具才是优秀程序员必备品质。

        这一篇也结束了,博主后续会更新课设系列、算法系列,再后期应该还要连更java系列和go系列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值