03-客户端服务器搭建(基础)
一、 配置文件
1. 资源文件
将客户端和服务器所需要的数据写在配置文件中,例如IP和端口
- 将服务器IP和PORT信息填入配置文件中;
- 将配置文件作为资源文件添加到项目中;
- 程序运行时加载配置文件中的数据;
2. 在QT Creator中新建一个项目,选择基类为QWidget
3. 在代码目录下创建一个client.config配置文件
(1)先做本地测试,设置本地回环IP地址127.0.0.1
127.0.0.1 # 回环IP地址
8888 # 端口号
- 接着在QtCreator中将此文件添加到资源文件中,这样无论项目怎么迁移,资源文件一直都在;(有用过QtCreator的添加图片资源就非常明白了)
- 添加前缀,区别于其他的资源文件;
(2)设置好资源文件后,就是将数据读取出来(用文件进行操作)
小技巧:定位到头文件的声明函数后可以使用alt + 回车,快速跳转到.cpp文件进行函数定义
// tcpclient.cpp
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QByteArray>
#include <QDebug>
#include <QMessageBox>
#include <QStringList>
TcpClient::TcpClient(QWidget *parent)
: QWidget(parent)
, ui(new Ui::TcpClient)
{
ui->setupUi(this);
loadConfig();
}
TcpClient::~TcpClient()
{
delete ui;
}
void TcpClient::loadConfig()
{
bool isOpenSF;
// 将路径赋值到file对象中
// 资源文件的写法 : + 前缀
QFile file(":/client.config");
// 打开文件, 传入参数:打开的方式(读写等等),
// 若成功打开会返回true
isOpenSF = file.open(QIODevice::ReadOnly);
if (isOpenSF)
{
// 将资源文件中的数据全部读取出来,作为字节数组存储
// 再将字节类型转换成字符串,但是这里不能直接进行转换,要变成 char* (const _CharT*)
QByteArray baData = file.readAll();
QString strData = baData.toStdString().c_str();
// qDebug() << strData;
// 读取完文件后,要关闭
file.close();
// 读取完文件后要解析,对字符串进行切割
// 将/r/n替换成空格
strData.replace("\r\n", " ");
// qDebug() << strData;
// 用空格分割, 返回QStringList
QStringList strList = strData.split(" ");
int strListSize = strList.size();
// for (int i = 0; i < strListSize; ++i)
// {
// // 将分割的值循环打印出来
// qDebug() << "<===>" << strList[i];
// }
m_strIP = strList.at(0); // IP 地址是字符串
m_usPort = strList.at(1).toUShort(); // 将字符串端口转换为无符号短整型
// qDebug() << "IP: " << m_strIP << " port: " << m_usPort;
}
else
{
// 若打开失败,提示错误
QMessageBox::critical(this, "open config", "open config failed");
}
}
2. 客户端实现
(1)TCP客户端连接服务器
- 先加载配置文件,接着产生socket,然后连接服务器;
- 连接服务器成功之后,分两步处理:接收服务器数据和发送数据给服务器;
(2)在项目文件(.pro)中,加上网络network模块
QT += core gui network
# 加完后就可以使用QT中的网络编程模块
- 在tcpclient.h头文件中添加:
#include <QTcpSocket>
// 连接服务器
// 默认读写,既可以往服务器中写也可以从服务器干读取数据
m_tcpSocket.connectToHost(QHostAddress(m_strIP), m_usPort);
(3)因为连接服务器成功会发出connected()信号,所以在头文件中添加槽函数对信号做处理
void TcpClient::connetServer()
{
// 信号与槽函数先进行连接关联
// 参数1 信号的发布者,m_tcpSocket,因为要通过m_tcpSocket来连接服务器
// 参数2 发布的是什么信号
// 参数3 信号的处理者,即tcpclient对象
// 参数4 信号的处理者用什么槽函数进行处理
connect(&m_tcpSocket, SIGNAL(connected())
, this, SLOT(showConnect()));
// 连接服务器
// 默认读写,既可以往服务器中写也可以从服务器干读取数据
m_tcpSocket.connectToHost(QHostAddress(m_strIP), m_usPort);
}
// 要与信号(connected)的处理函数进行关联才行
void TcpClient::showConnect()
{
// 假设连接服务器成功之后,就会发出connected()信号,接着就可以通过QMessageBox将消息返回
QMessageBox::information(this, "连接服务器", "连接服务器成功");
}
3. 服务器搭建
(1)新建项目
- 服务器进行与客户端的连接要绑定和监听,需要提供IP和端口号
- 创建并补充server.config配置文件,且将其添加到项目中,同前面一样
(2)服务器实现过程
- 加载配置文件
- 连接数据库
- 接收客户端的连接
QTcpServer 监听及接收客户端的连接
QTcpSocket 和客户端数据交互
(3)继承QTcpServer类,创建一个MyTcpServer派生类
-
为了能让派生的类支持信号和槽,要做两个步骤
-
要继承public QObject,但是继承的QTcpServer已经继承了这个类,所以就不用写了;
-
添加Q_OBJECT
-
(4)接收客户端的连接,首先要进行监听,这里采用单例模式
这样做,当后面需要MyTcpServer的地方,可以直接通过类名调用getInstance(),获得静态的局部对象进行操作
// 这样做,当后面需要MyTcpServer的地方,可以直接通过类名调用getInstance(),获得静态的局部对象进行操作
// 静态
MyTcpServer &MyTcpServer::getInstance()
{
// 定义一个静态的MyTCPServer的对象
static MyTcpServer instance;
// 将此对象作为引用返回
return instance;
}
(5)那么如何知道客户端连接了
void incomingConnection(qintptr handle) override; // 监听后,一旦有客户端连接,就会自动调用此函数(这是虚函数需要重写)
// 利用MyTcpServer进行监听之后,只要有客户端连接过来,
// 就会自动调用incomingConnection函数处理
void MyTcpServer::incomingConnection(qintptr handle)
{
qDebug() << "new client connected";
}
(6)完成这些步骤后
先启动服务器端再启动客户端