写在前面
博主要做的项目涉及到服务器和客户端,所以要学习一下如何用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系列。