本文未经授权,禁止转载
一、项目概述
1.1 项目介绍
该项目是基于QT的一个网盘项目,会持续更新。
1.2 项目设计到的知识
学习该项目需要提前掌握
1、C++的面向对象编程(封装继承多态肯定是要熟悉的)
2、网络编程(TCP协议、Socket编程等)
Tcp协议: 后面会发笔记
Socket编程:简单实现的socket_你一般代码中是怎么实现socket的?-CSDN博客
3、QT的一些基础知识(信号槽机制,常用的控件等)
4、数据库的相关知识(MySql的基本命令)
1.3 项目编程环境
Qt Creator 4.11.1 (Community)
1、 安装包:
链接:https://pan.baidu.com/s/1eD-DobJAF1497sY--XaJ2w?pwd=1223
提取码:1223
--来自百度网盘超级会员V4的分享
2、安装教程(自己搜一个,安装不上,那还学什么项目)
【安装篇】Qt 安装详细教程(Windows版)_qt安装-CSDN博客
1.4 创建项目
1、文件 --> 新建项目(或者是欢迎 -- > new)
2、选择类型
3、项目名称(客户端:NetdiskClient ;服务器:NetdiskServer)
4、类名与基类(客户端:Client ;服务器:Server),基类都是 QWidget
剩下没说到的都是下一步。
二、客户端申请连接(NetdiskClient项目文件)
客户端的核心目标:
1、与服务器建立连接
2、建立连接后,与服务器进行通信
2.1 与服务器申请建立连接
首先我们需要与客户端建立连接,只有建立连接之后,才能进行后续的一系列操作(进行数据交互、通信等)
在QT中使用网络模块,需要在项目文件 .pro 中添加 network模块。
在该文件下,添加一个 network。
有网络编程基础的话,就能明白,建立网络通信,需要使用Socket建立连接,而QT内部已经对Socket进行了封装,使用QTcpSocket类,调用类中的connectToHost函数就能申请建立连接
我们来看connectToHost函数的定义。
void connectToHost(const QHostAddress &address, // ip地址
quint16 port, // 端口号
OpenMode mode = ReadWrite);
这里我们只看前两个参数:
const QHostAddress &address:QHostAddress类型的IP地址
quint16 port:quint16,16位的端口号,2个字节(0~65535)
也就是说,我们只要传入服务器的IP地址和端口号即可申请与服务器进行连接,因此我们需要创建一个QTcpSocket类的对象,然后调用connectToHost函数将IP地址和端口号传入就能申请连接。这里我们将主机IP(127.0.0.1)和端口号(5000)传入即可。
QHostAddress("127.0.0.1")是将字符串类型转为QHostAddress类型。
// 使用tcpSocket 进行连接
// 创建一个 QTcpSocket的对象 s
QTcpSocket s;
// 对象s调用connectToHost函数,申请建立连接
s.connectToHost(QHostAddress("127.0.0.1"),5000);
上面的两行代码,就完成了与服务器申请建立连接的内容,但是,如果项目这样写的话,太low了,是不符合要求的。因此我们要对上面的代码进行改进(提升逼格)。
优化的内容:
1、将IP地址和端口号放到配置文件中,我们只需要实现一个加载配置文件的函数,就能知道要申请谁建立连接,后续还可以在配置文件中添加其他内容,对配置文件修改等。
2、将加载配置文件的函数进行封装,将IP和端口号设为成员变量,方便后续调用。
3、将网络连接中的QTcpSocket类对象设为成员变量,方便后续调用。
4、定义一个槽函数,在连接成功后进行提示。
5、实现单例模式。
2.2 添加配置文件
2.2.1 新建资源文件
1、右键项目 --> Add New
2、选择 QT资源文件 Qt Resource File,然后点击Choose。
3、 设置资源文件名称为 config(配置文件)
2.2.2 添加前缀
到这里之后,就会发向项目中多了一个文件夹,而且自动弹出了该界面
1、添加前缀:点击 Add Prefix之后,灰色窗口就会变成下面的样子
我们将前缀改为 / 即可
2.2.3 添加配置文件
1、添加文件:点击Add Files
2、新建配置文件:点击Add Files,就会弹出新建文件窗体,新建一个client.config即可
3、给配置文件中添加内容,新建之后,按 ctrl s 保存,会发现client.config已经添加到资源文件中,打开该配置文件,输入IP地址和端口号即可。
这样我们就完成了配置文件的添加。
2.3 加载配置文件
2.3.1 将IP地址和端口号设置为成员变量
我们要在加载配置文件的函数中,读取IP地址和端口号,在其他函数中,利用读取到的IP地址和端口号进行连接,因此,需要将这两个变量设置为成员变量,以便于后续的调用。
client.h
# Client 类
class Client : public QWidget
{
Q_OBJECT
public:
private:
// IP地址
QString m_strIP;
// 端口号
quint16 m_uintPort;
};
2.3.2 将加载配置文件的函数进行封装,以读取配置文件
在 client.h中声明一个 loadConfig() 函数,在client.cpp中进行实现。
读取配置的步骤如下:
1、使用 QFile 类操作文件(路径:“:+ 前缀+文件名”), 需要导入头文件。
#include <QFile>
// 获取文件路径
QFile file(":/client.config");
2、创建一个 QFile 对象file,使用open()函数只读打开文件。(打开失败提示报错)
// 只读打开
if(!file.open(QIODevice::ReadOnly))
{
// 打开失败提示错误
QMessageBox::critical(this,"打开配置文件","打开配置文件失败");
return;
}
3、使用readAll()函数 读取配置文件的内容,会返回一个 QByteArray 对象。
// 读取配置文件信息
QByteArray baData = file.readAll();
// 测试--打印读取到的信息
qDebug()<<"baData" <<baData;
读到的内容
4、将QByteArray类型的数据 转换为 QString类型,并 使用split()函数,按照 \rn 进行拆分出每行的字符串。会返回一个QStringList 的列表。
// 将QByteArray 强转为字符串
QString strData = QString(baData);
// 将字符串按照 “\r\n” 进行切分
QStringList strList = strData.split("\r\n");
5、QStringList按下标取出每行数据,使用.toUShort()端口号转成数字 quint16
// 读取IP,使用成员变量存储
m_strIP = strList.at(0);
// 读取端口号--端口号是2个字节16位的short类型,范围是0~65535
m_uintPort = strList.at(1).toUShort();
// 测试--分别打印IP和端口号
// qDebug()<<"strIP: "<<m_strIP;
// qDebug()<<"uintPort: "<<m_uintPort;
6、关闭文件 close() 函数。
2.3.3 完整代码
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Client; }
QT_END_NAMESPACE
class Client : public QWidget
{
Q_OBJECT
public:
// 构造函数
Client(QWidget *parent = nullptr);
~Client();
// 加载配置文件的声明
void loadConfig();
private:
Ui::Client *ui;
// IP地址
QString m_strIP;
// 端口号
quint16 m_uintPort;
};
#endif // CLIENT_H
client.cpp
#include "client.h"
#include "ui_client.h"
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QHostAddress>
// 构造函数
Client::Client(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Client)
{
ui->setupUi(this);
// 加载配置文件
loadConfig();
}
// 析构函数
Client::~Client()
{
delete ui;
}
// 加载配置文件
void Client::loadConfig()
{
// 获取文件路径
QFile file(":/client.config");
// 只读打开
if(!file.open(QIODevice::ReadOnly))
{
// 打开失败提示错误
QMessageBox::critical(this,"打开配置文件","打开配置文件失败");
return;
}
// 读取配置文件信息
QByteArray baData = file.readAll();
// 测试--打印读取到的信息
// qDebug()<<"baData" <<baData;
// 将QByteArray 强转为字符串
QString strData = QString(baData);
// 将字符串按照 “\r\n” 进行切分
QStringList strList = strData.split("\r\n");
// 读取IP
m_strIP = strList.at(0);
// 读取端口号--端口号是2个字节16位的short类型,范围是0~65535
m_uintPort = strList.at(1).toUShort();
// 测试--分别打印IP和端口号
// qDebug()<<"strIP: "<<m_strIP;
// qDebug()<<"uintPort: "<<m_uintPort;
// 关闭文件
file.close();
}
2.4 与服务器申请连接
2.4.1 将网络连接中的QTcpSocket类对象设为成员变量
定义一个 QTcpsocket类型的成员变量 m_tcpSocket,并设置一个公有方法getTcpSocket()获取该属性。
client.h 声明
// 获取网络连接的声明
QTcpSocket& getTcpSocket();
private:
// 设置网络连接的成员变量
QTcpSocket m_tcpSocket;
client.cpp 实现
// 获取网络连接
QTcpSocket& Client::getTcpSocket()
{
return m_tcpSocket;
}
2.4.2 使用tcpSocket 与服务器申请连接
使用m_tcpSocket调用 connectToHost函数,参数为ip 和端口号(ip 需要转为QHostAddress)。
在client.cpp的构造函数中调用
// 使用tcpSocket 与服务器申请连接
m_tcpSocket.connectToHost(QHostAddress(m_strIP),m_uintPort);
2.4.3 定义槽函数并关联,提示连接成功
在连接成功后,QTcpSocket会发出一个connected的信号,表示已经连接成功,因此我们需要定义一个槽函数,与该信号关联,一旦连接成功,关联的槽函数会提示连接成功了。
client.h 声明
// 槽函数-连接成功展示
void showConnected();
client.cpp 实现:QMessageBox对话框进行提示。
// 连接成功展示
void Client::showConnected()
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(this,"建立连接","连接成功");
}
在client.cpp的构造函数使用 connect()函数将信号与槽函数进行关联
// 连接成功后,会返回一个连接成功的信号,使用信号槽接收,并显示连接成功
connect(&m_tcpSocket,&QTcpSocket::connected,this,&Client::showConnected);
2.4.4 完整代码
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Client; }
QT_END_NAMESPACE
class Client : public QWidget
{
Q_OBJECT
public:
// 构造函数
Client(QWidget *parent = nullptr);
~Client();
// 加载配置文件
void loadConfig();
// 槽函数-连接成功展示
void showConnected();
// 获取网络连接
QTcpSocket& getTcpSocket();
private:
Ui::Client *ui;
// IP地址
QString m_strIP;
// 端口号
quint16 m_uintPort;
// 设置网络连接
QTcpSocket m_tcpSocket;
};
#endif // CLIENT_H
client.cpp
#include "client.h"
#include "ui_client.h"
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QHostAddress>
// 构造函数
Client::Client(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Client)
{
ui->setupUi(this);
// 加载配置文件
loadConfig();
// 使用tcpSocket 进行连接
m_tcpSocket.connectToHost(QHostAddress(m_strIP),m_uintPort);
// 连接成功后,会返回一个连接成功的信号,使用信号槽接收,并显示连接成功
connect(&m_tcpSocket,&QTcpSocket::connected,this,&Client::showConnected);
}
// 析构函数
Client::~Client()
{
delete ui;
}
// 加载配置文件
void Client::loadConfig()
{
// 获取文件路径
QFile file(":/client.config");
// 只读打开
if(!file.open(QIODevice::ReadOnly))
{
// 打开失败提示错误
QMessageBox::critical(this,"打开配置文件","打开配置文件失败");
return;
}
// 读取配置文件信息
QByteArray baData = file.readAll();
// 测试--打印读取到的信息
// qDebug()<<"baData" <<baData;
// 将QByteArray 强转为字符串
QString strData = QString(baData);
// 将字符串按照 “\r\n” 进行切分
QStringList strList = strData.split("\r\n");
// 读取IP
m_strIP = strList.at(0);
// 读取端口号--端口号是2个字节16位的short类型,范围是0~65535
m_uintPort = strList.at(1).toUShort();
// 测试--分别打印IP和端口号
// qDebug()<<"strIP: "<<m_strIP;
// qDebug()<<"uintPort: "<<m_uintPort;
// 关闭文件
file.close();
}
// 连接成功展示
void Client::showConnected()
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(this,"建立连接","连接成功");
}
// 获取网络连接
QTcpSocket& Client::getTcpSocket()
{
return m_tcpSocket;
}
2.5 实现单例模式
2.5.1 单例模式介绍
保证一个类只有一个实例,控制某些共享资源的访问权限。创建了一个对象后,之后再创建一个新对象,此时会获得之前已创建的对象,而不是一个新对象。
为该实例提供一个全局访问节点。单例模式允许在程序的任何地方访问特定对象。但是它可以保护该实例不被其他代码覆盖。
这里暂时不对设计模式进行过多赘述,请自行学习单例模式的作用。
2.5.2 实现单例模式
1、定义获取单例对象的静态成员函数 getlnstance()
该静态成员函数就是一个全局的访问节点,静态成员函数,不需要使用对象访问,可以通过 作用域::getlnstance() 调用,来创建对象。(其他途径已经被我们删除了,所以只能通过该节点进行访问)返回引用类型,可以防止拷贝构造。
client.h中声明函数
// 定义静态成员函数 获取单例对象
static Client& getInstance();
2、getlnstance()函数返回静态局部对象的引用
返回一个静态的局部对象的引用,有俩种好处,一是静态变量只能赋值一次,能够有效防止,多次调用创建多个对象,二是返回引用可以防止进行拷贝构造。
一开始是通过if(instance==null) 开判断是否创建了该实例,但是这种情况存在线程安全的问题,比如在实际情况中,存在多个线程同时调用getInstance(),此时多个线程判断都是成立的,就会创建多个实例,这种情况下就得添加锁或者互斥量。来保证线程安全
而返回一个静态的局部对象的引用,在C++11之后,默认支持线程安全。
在client.cpp中实现
// 实现静态成员函数 获取单例对象
Client &Client::getInstance()
{
// 返回静态局部引用对象,防止调用拷贝构造,静态变量只能赋值一次
static Client instance;
return instance;
}
3、私有化构造函数、删除拷贝构造函数和拷贝赋值运算符,防止通过这些途径创建实例
将可能创建实例的方法全部私有化或删除,确保只能通过getInstance()这一个方法创建实例
private:
// 防止通过以下途径创建实例。
// 私有化构造函数
Client(QWidget *parent = nullptr);
// 删除拷贝构造
Client(const Client& instance) = delete;
// 删除拷贝赋值 = 运算符
Client operator= (const Client&) =delete;
4、创建实例时通过 getlnstance() 创建
构造函数已经私有化了,只能通过getInstance()来创建实例对象。
在main.c 中调用
#include "client.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Client w; // 构造函数私有化了,无法使用
// w.show();
// 使用静态成员函数 获取单例对象
Client::getInstance().show();
return a.exec();
}
2.6 客户端完整代码
到这里,我们就完成了客户端申请连接的代码。
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Client; }
QT_END_NAMESPACE
class Client : public QWidget
{
Q_OBJECT
public:
~Client();
// 加载配置文件
void loadConfig();
// 槽函数-连接成功展示
void showConnected();
// 获取网络连接
QTcpSocket& getTcpSocket();
// 定义静态成员函数 获取单例对象
static Client& getInstance();
private:
Ui::Client *ui;
// IP地址
QString m_strIP;
// 端口号
quint16 m_uintPort;
// 设置网络连接
QTcpSocket m_tcpSocket;
// 防止通过以下途径创建实例。
// 私有化构造函数
Client(QWidget *parent = nullptr);
// 删除拷贝构造
Client(const Client& instance) = delete;
// 删除拷贝赋值运算符
Client operator= (const Client&) =delete;
};
#endif // CLIENT_H
client.cpp
#include "client.h"
#include "ui_client.h"
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QHostAddress>
Client::Client(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Client)
{
ui->setupUi(this);
// 加载配置文件
loadConfig();
// 使用tcpSocket 进行连接
m_tcpSocket.connectToHost(QHostAddress(m_strIP),m_uintPort);
// 连接成功后,会返回一个连接成功的信号,使用信号槽接收,并显示连接成功
connect(&m_tcpSocket,&QTcpSocket::connected,this,&Client::showConnected);
}
Client::~Client()
{
delete ui;
}
// 加载配置文件
void Client::loadConfig()
{
// 获取文件路径
QFile file(":/client.config");
// 只读打开
if(!file.open(QIODevice::ReadOnly))
{
// 打开失败提示错误
QMessageBox::critical(this,"打开配置文件","打开配置文件失败");
return;
}
// 读取配置文件信息
QByteArray baData = file.readAll();
// 测试--打印读取到的信息
// qDebug()<<"baData" <<baData;
// 将QByteArray 强转为字符串
QString strData = QString(baData);
// 将字符串按照 “\r\n” 进行切分
QStringList strList = strData.split("\r\n");
// 读取IP
m_strIP = strList.at(0);
// 读取端口号--端口号是2个字节16位的short类型,范围是0~65535
m_uintPort = strList.at(1).toUShort();
// 测试--分别打印IP和端口号
// qDebug()<<"strIP: "<<m_strIP;
// qDebug()<<"uintPort: "<<m_uintPort;
// 关闭文件
file.close();
}
// 连接成功展示
void Client::showConnected()
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(this,"建立连接","连接成功");
}
// 获取网络连接
QTcpSocket& Client::getTcpSocket()
{
return m_tcpSocket;
}
// 实现静态成员函数 获取单例对象
Client &Client::getInstance()
{
// 返回静态局部引用对象,防止调用拷贝构造,静态变量只能赋值一次
// 在C++11之后,默认支持线程安全
static Client instance;
return instance;
}
main.h
#include "client.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Client w; // 构造函数私有化了,无法使用
// w.show();
// 使用静态成员函数 获取单例对象
Client::getInstance().show();
return a.exec();
}
三、服务器监听连接请求(NetdiskServer项目文件)
服务器的核心目标:
1、监听客户端发来的连接请求
2、建立连接后,与处理客户端的各种请求
3.1 服务器监听连接请求
首先服务器需要监听客户端发来的建立连接请求,只有建立连接之后,才能进行后续的一系列操作(进行数据交互、通信等)
在QT中使用网络模块,需要在项目文件 .pro 中添加 network模块。
在该文件下,添加一个 network。
有网络编程基础的话,就能明白,建立网络通信,服务器需要监听客户端发来的申请连接的请求,而QT内部已经进行了封装,使用QTcpServer类,调用类中的listen()函数就能申请建立连接
我们来看listen()函数的定义。
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
参数为 IP地址和端口号
也就是说,我们只要在服务器中使用监听的listen()函数,并传入监听的IP地址和端口号即可实现监听,因此我们需要创建一个QTcpServer类的对象,然后调用listen函数将IP地址和端口号传入就能监听连接。这里我们将主机IP(127.0.0.1)和端口号(5000)传入即可。
// 进行监听
QTcpServer s;
s.listen(QHostAddress("127.0.0.1"),5000);
上面的两行代码,就完成了与服务器监听客户端申请建立连接的内容,但是,如果项目这样写的话,太low了,是不符合要求的。因此我们要对上面的代码进行改进(提升逼格)。
优化的内容:
1、将IP地址和端口号放到配置文件中,我们只需要实现一个加载配置文件的函数,就能知道要申请谁建立连接,后续还可以在配置文件中添加其他内容,对配置文件修改等。
2、将加载配置文件的函数进行封装,将IP和端口号设为成员变量,方便后续调用。
3、定义一个MyTcpServer类,并继承QTcpServer类,实现MyTcpServer实例对象的单例模式。
4、将incomingConnection()函数进行重写,实现新的客户端连接成功后,进行提示。
5、定义一个MyTcpSocket类,并继承QTcpSocket类,方便后续对Socket进行操作。
6、添加一个成员变量QList<MyTcpSocket*> m_tcpSocketList,一个存储Socket描述符的列表,用来存储多个客户端Socket,以解决多个客户端进行连接的情况。
3.2 添加配置文件
同 2.2 添加配置文件
3.3 加载配置文件
同 2.3 加载配置文件
3.4 定义MyTcpServer类,实现单例模式。
新建一个MyTcpServer类,并继承QTcpServer类,实现MyTcpServer实例对象的单例模式。
Server类使用 MyTcpServer调用listen 函数
MyTcpServer.h
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QWidget>
#include <QTcpServer>
#include <mytcpsocket.h>
class MyTcpServer : public QTcpServer
{
Q_OBJECT // 由于接收信号
public:
// 获取单例对象的声明
static MyTcpServer& getInstance();
private:
// 私有化构造函数
MyTcpServer();
// 删除拷贝构造
MyTcpServer(const MyTcpServer& instance) = delete;
// 删除 拷贝赋值运算符
MyTcpServer operator =(const MyTcpServer&) =delete;
};
#endif // MYTCPSERVER_H
MyTcpServer.cpp
#include "mytcpserver.h"
// 构造函数
MyTcpServer::MyTcpServer()
{
}
// 获取单例对象
MyTcpServer &MyTcpServer::getInstance()
{
// 设置静态局部引用
static MyTcpServer instance;
// 返回单例对象
return instance;
}
Server类的构造函数中使用 MyTcpServer调用listen 函数
// 进行监听
MyTcpServer::getInstance().listen(QHostAddress(m_IP),m_Port);
3.5 重写incomingConnection()函数
在服务器与客户端成功建立连接后,会触发incomingConnection()函数,我们想要连接成功后,进行提示,因此需要将incomingConnection()函数进行重写,以实现新的客户端连接成功后,进行提示。
在 MyTcpServer.h中声明,参数暂时可以不看
// 重写连接成功的槽函数
void incomingConnection(qintptr handle);
在 MyTcpServer.cpp中实现,利用QMessageBox对话框进行提示。
// 重写连接成功的信号槽函数,并将连接成功的socket 放入 m_tcpSocketList 列表中。
void MyTcpServer::incomingConnection(qintptr handle)
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(msgBox,"客户端建立连接","新客户端连接成功");
}
3.6 存储客户端的Socket信息
在实际情况中,不可能只有一个客户端与服务器进行连接,应该是有很多客户端与服务器进行连接的。因此我们要定义一个数据结构来存储多个客户端的连接信息。
1、定义一个MyTcpSocket类,并继承QTcpSocket类,方便后续对Socket进行操作。
2、添加一个成员变量QList<MyTcpSocket*> m_tcpSocketList,一个存储Socket描述符的列表,用来存储多个客户端Socket,以解决多个客户端进行连接的情况。
3、在incomingConnection()函数中,将当前连接的客户端socket指针放入该列表
3.6.1 定义MyTcpSocket类
定义一个MyTcpSocket类,并继承QTcpSocket类即可
MyTcpSocket.h
#ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H
#include <QWidget>
#include <QTcpSocket>
class MyTcpSocket:public QTcpSocket
{
Q_OBJECT
public:
MyTcpSocket();
};
#endif // MYTCPSOCKET_H
MyTcpSocket.cpp
#include "mytcpsocket.h"
MyTcpSocket::MyTcpSocket()
{
}
3.6.2 添加存储客户端连接的列表
在 MyTcpServer.h中声明成员变量,QList<MyTcpSocket*> m_tcpSocketList
// 定义连接成功的客户端 socket 的列表
QList<MyTcpSocket*> m_tcpSocketList;
3.6.3 将连接成功的客户端存入列表中。
在incomingConnection()函数中,将当前连接的客户端socket指针放入该列表
在 MyTcpServer.cpp的incomingConnection()函数中实现,handle中存放了客户端的socket信息。
// 重写连接成功的信号槽函数,并将连接成功的socket 放入 m_tcpSocketList 列表中。
void MyTcpServer::incomingConnection(qintptr handle)
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(msgBox,"客户端建立连接","新客户端连接成功");
// 将新连接的客户端socket 存到 m_tcpSocketList 列表中。
MyTcpSocket* tcpSocket = new MyTcpSocket;
tcpSocket->setSocketDescriptor(handle);
m_tcpSocketList.append(tcpSocket);
}
3.7 服务器完整代码
mytcpsocket.h
#ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H
#include <QWidget>
#include <QTcpSocket>
class MyTcpSocket:public QTcpSocket
{
Q_OBJECT
public:
MyTcpSocket();
};
#endif // MYTCPSOCKET_H
mytcpsocket.cpp
#include "mytcpsocket.h"
MyTcpSocket::MyTcpSocket()
{
}
mytcpserver.h
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QWidget>
#include <QTcpServer>
#include <mytcpsocket.h>
class MyTcpServer : public QTcpServer
{
Q_OBJECT
public:
// 获取单例对象
static MyTcpServer& getInstance();
// 重写连接成功的槽函数
void incomingConnection(qintptr handle);
private:
// 构造私有化
MyTcpServer();
// 删除拷贝构造
MyTcpServer(const MyTcpServer& instance) = delete;
// 删除 拷贝赋值运算符
MyTcpServer operator =(const MyTcpServer&) =delete;
// 定义连接成功的客户端 socket 的列表
QList<MyTcpSocket*> m_tcpSocketList;
};
#endif // MYTCPSERVER_H
mytcpserver.cpp
#include "mytcpserver.h"
#include <QMessageBox>
// 构造函数
MyTcpServer::MyTcpServer()
{
}
// 获取单例对象
MyTcpServer &MyTcpServer::getInstance()
{
// 设置静态局部引用
static MyTcpServer instance;
// 返回单例对象
return instance;
}
// 重写连接成功的信号槽函数,并将连接成功的socket 放入 m_tcpSocketList 列表中。
void MyTcpServer::incomingConnection(qintptr handle)
{
// 消息对话框,展示连接成功。
QMessageBox* msgBox = new QMessageBox;
msgBox->information(msgBox,"客户端建立连接","新客户端连接成功");
// 将新连接的客户端socket 存到 m_tcpSocketList 列表中。
MyTcpSocket* tcpSocket = new MyTcpSocket;
tcpSocket->setSocketDescriptor(handle);
m_tcpSocketList.append(tcpSocket);
}
server.h
#ifndef SERVER_H
#define SERVER_H
#include <QWidget>
#include "mytcpserver.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Server; }
QT_END_NAMESPACE
class Server : public QWidget
{
Q_OBJECT
public:
Server(QWidget *parent = nullptr);
~Server();
// 加载本地文件
void loadConfig();
private:
Ui::Server *ui;
QString m_IP;
quint16 m_Port;
};
#endif // SERVER_H
server.cpp
#include "server.h"
#include "ui_server.h"
#include <QFile>
#include <QMessageBox>
#include <QDebug>
Server::Server(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Server)
{
ui->setupUi(this);
// 加载配置文件
loadConfig();
// 进行监听
MyTcpServer::getInstance().listen(QHostAddress(m_IP),m_Port);
}
Server::~Server()
{
delete ui;
}
// 加载配置文件
void Server::loadConfig()
{
// 获取文件路径
QFile file(":/server.config");
// 打开文件
if(!file.open(QIODevice::ReadOnly))
{ // 打开失败--错误提示
QMessageBox::critical(this,"打开配置文件","打开配置文件失败");
return;
}
// 读取文件内容
QByteArray baData = file.readAll();
// 转换格式为 QString
QString strData = QString(baData);
// 将字符串按照 "\r\n" 进行分割
QStringList strList = strData.split("\r\n");
// 获取IP地址
this->m_IP = strList.at(0);
// 获取端口号
this->m_Port = strList.at(1).toShort();
// 测试打印
// qDebug()<<"IP:"<<m_IP;
// qDebug()<<"Port:"<<m_Port;
}
main.cpp
#include "server.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Server w;
w.show();
return a.exec();
}