此项目是使用C++开发的QT局域网聊天室。该项目功能是和局域网内的好友进行聊天。包含的技术点有TCP传输数据(socket套接字),sqlite数据库存储数据,多线程并发服务器,QT自定义控件等技术点。
下面展示部分技术点代码。
QT的TCP客户端(与服务器建立连接)
1)添加配置
添加网络模块,添加之后保存文件
QT += network
2
)
QTcpSocket
类
socket是系统给我们分配的用于tcp发送接收数据的资源。
#include <QTcpSocket>
#include <QHostAddress>
QTcpSocket* socket = new QTcpSocket(this);//创建socket对象,实参是对象的父节点,QT的内
存
//管理机制,QT会构建一个树形结构,当父节点删除时会删除所有的子节点。
//连接主机QHostAddress是主机地址类,需要字符串ip作为实参,这里是创建了一个QHostAddress的匿
名对象
//参数2是端口,ip地址是找主机电脑的,端口是在电脑里找服务端程序的。
socket->connectToHost(QHostAddress("192.168.31.196"), 55555);
//connectToHost函数执行,程序也不会阻塞,tcp不会马上连接成功,当连接成功之后socket对象会发出
一个connected()信号通知我们
connect(socket, SIGNAL(connected()), this, SLOT(socket_connect()));
//连接成功后,断开连接发送disconnected()
connect(socket, SIGNAL(disconnected()), this, SLOT(socket_disconnect()));
//socket收到数据时发出readyRead()
connect(socket, SIGNAL(readyRead()), this, SLOT(socket_read()));
void MainWindow::socket_read()
{
//socket->readAll();读取收到的所有数据,返回值是一个字节数组对象,字节数组是以字节为单位
的数组,注意字节数组不是字符串。byte就是一个字节的整数
QByteArray data = socket->readAll();
//QString::fromLocal8Bit(data)将字节数组转换成QString对象,前提是收到的数据一定是一个
字符串。
ui->textBrowser->append(QString::fromLocal8Bit(data));
}
void MainWindow::on_pushButton_send_clicked()
{
QString data = ui->lineEdit_msg->text();
QByteArray bdata = data.toLocal8Bit();//将字符串转换成字节数组,socket的数据收发都
是字节数组
//bdata.data()返回指向字节数组中数据的指针
//bdata.size()返回字节数组的长度 size length count一般都是用来表示长度的单词
//socket->write(bdata.data(), bdata.size());
socket->write(bdata);
}
QT的tcp服务端(与客户端建立连接)
1
)获得本机
ip
//QHostInfo主机信息类
QString localHostName = QHostInfo::localHostName();//获得主机名称
QHostInfo info = QHostInfo::fromName(localHostName);//根据主机名称获得主机信息
QList<QHostAddress> list = info.addresses();//获得主机地址,因为一台计算机大概率不止一个
ip
Qstring ip;
for(int i = 0;i < list.size();i++)
{
//list[i]获得地址数组中的i元素,QHostAddress对象
//protocol获得地址的协议类型,==QAbstractSocket::IPv4Protocol比较地址是否是ipv4的地
址
if(list[i].protocol()==QAbstractSocket::IPv4Protocol)
{
//字符串拼接
ip+="*";
ip+=list[i].toString();
}
}
ui->lable_ip->setText(ip); //显示在画面上
2
)
QTcpServer
QTcpServer类用于监听客户端的连接,每当有一个客户端连接到服务端,都会生成一个新的
QTcpSocket对象与客户端通信。
QTcpServer* server; //创建对象,实参是内存管理的父节点
QTcpSocket* socket; //创建客户端套接字(临时的,后期会改成多线程并发服务器)
server = new QTcpServer(this);
//监听主机的ip地址和端口号,QHostAddress::Any表示监听主机的所有ip
server->listen(QHostAddress::Any, 55555);
//当有新的客户端连接时,server对象会发出newConnection信号
connect(server, SIGNAL(newConnection()), this, SLOT(newConnect()));
void MainWindow::newConnect()
{
qDebug()<<”客户端连接我了”;
socket = server->nextPendingConnection();//获得与客户端通信的QTcpSocket对象
//连接socket的信号与槽
connect(socket, SIGNAL(disconnected()), this, SLOT(socket_disconnect()));
connect(socket, SIGNAL(readyRead()), this, SLOT(socket_read()));
}
数据库API
sqlite数据库用于把账号信息存储起来。
配置
QT += sql
开关数据库
#include <QSqlDatabase>
//QSqlDatabase类主要用于打开和关闭数据库,不负责数据库的具体操作。增删改查它都不管。
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");//加载sqlite驱动,使用不同的
数据库加载不同的驱动即可,驱动都是qt提供好的。
db.setHostName("testdb");//设置数据库的主机名称
db.setDatabaseName("testdb.db");//设置数据库名称,sqlite数据库以文件形式存在,所以这里是
文件名
db.open();//打开数据库
db.close();//关闭数据库
数据库操作
------
创建表
对于数据库所有增删改查的操作,都使用QSqlQuery来实现。QSqlDatabase只负责加载驱动、打开关闭。
#include <QSqlQuery>
QSqlQuery query;//QSqlQuery类负责数据库的增删改查操作
//执行sql语句 create table if not exists user 在user表不存在的情况下创建user表
/*
userid integer primary key autoincrement 字段userid,整型,主键,自动增长,在本次项目
中,userid字段就是用户的账号,每当新插入一个用户,都会生成一个新的userid
*/
query.exec("create table if not exists user("
"userid integer primary key autoincrement,"
"password varchar(20),"
"nickname varchar(20),"
"headid integer);" );
//插入一个原始用户因为希望用户的id是用100001开始,所以原始用户的id是100000
query.exec("insert into user values(100000,'abc123','admin',0)");
数据库操作
------
插入数据
QSqlQuery query;
//exec只能执行固定的sql语句,如果语句中需要带一些参数,那么不能直接使用exec
//prepare可以设置一个带参数的sql语句,?表示参数
query.prepare("insert into user(password, nickname, headid) values(?,?,?)");
//bindValue 是给prepare中sql的?绑定参数的 0代表第一个?,1代表第二个?
query.bindValue(0, e.password);
query.bindValue(1, e.nickname);
query.bindValue(2, e.headId);
query.exec();//执行绑定参数后的sql语句
数据库操作
------
查询数据
#include <QSqlRecord>
#include <QVariant>
QSqlQuery query;
query.exec("select * from user");//查询所有的数据
//查询成功后,所有的结果都在query对象中,以链表形式(假设)存储,所以我们要获得所有的数据需要遍
历链表
//链表是有空头的链表
while(query.next())//query.next()移动到下一个节点,因为头是空的,所以第一次就跳过空头,返
回true表示有下一个节点,每个节点存放一行数据
{
QSqlRecord record = query.record();//QSqlRecord中存放了一行的数据
//QVariant是QT的泛型类,可以表示任何类型数据,也可以转换成任何类型数据
QVariant userId = record.value("userid");//通过字段名获取值
int userIdInt = userId.toInt();//将QVariant类型转换成整型
QString name = query.record().value("nickname").toString();
int headId = query.record().value("headid").toInt();
}
数据库查询
------
条件查询
QSqlQuery query;
query.prepare("select * from user where userid = ? and password = ?");
query.bindValue(0, e.id);
query.bindValue(1, e.password);
query.exec();
bool ok1 = query.exec();
if(query.next())
{
//登录成功
ok = true;
e.nickName = query.record().value("nickname").toString();
e.headId = query.record().value("headid").toInt();
}
else
{
//登录失败
ok = false;
}
数据库查询
------
查询
id
QSqlQuery query;
query.exec("SELECT LAST_INSERT_ROWID()");
if(query.next())
{
id = query.record().value(0).toInt();
}
自定义多线程QTcpServer
用于处理多个客户端同时连接服务器,保证每个用户能够正常收发数据。
QTcpSever* server = new QTcpServer(this);//创建对象,实参是内存管理的父节点
//监听主机的ip地址和端口号,QHostAddress::Any表示监听主机的所有ip
server->listen(QHostAddress::Any, 55555);
//当有新的客户端连接时,server对象会发出newConnection信号
connect(server, SIGNAL(newConnection()), this, SLOT(newConnect()));
void MainWindow::newConnect()
{
QTcpSocket* client;
client = server->nextPendingConnection();//获得与客户端通信的QTcpSocket对象
//连接socket的信号与槽
connect(client, SIGNAL(disconnected()), this, SLOT(socket_disconnect()));
connect(client, SIGNAL(readyRead()), this, SLOT(socket_read()));
}
上面的方法是在主线程中创建QTcpSocket对象,这个对象在子线程中不能使用。
多线程并发服务器需要使用这种方法,由于代码有点繁琐,就不放上面了,需要的加我联系方式。
(vx:gfx2503654393)
项目展示
如上图,xh给xm发送消息12345,并收到xm发来的消息ackalna。
这只是部分功能的代码分享,其他的就先不展示了。
最后的最后,感谢大家的支持,请求各路英雄豪杰多多打赏,多多关注,多多点赞,我是只发布高质量文章的 better-code 。