创作本文目的:记录自己的学习历程
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_start_clicked();
//newConnection信号对应槽函数
void on_newConnection_slot();
//readyRead信号对应槽函数
void on_readyRead_slot();
//自定义广播函数
void send(QString mes);
private:
Ui::Widget *ui;
//定义一个服务器指针
QTcpServer *server;
//定义客户端指针,存放连接的客户端
QList<QTcpSocket *> socketList;
//定义bool类型数据,表示连接状态
bool isOk = false;
};
#endif // WIDGET_H
.cpp文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置标题栏
QWidget::setWindowTitle("TCP服务器");
//实例化服务器
server = new QTcpServer(this);
}
Widget::~Widget()
{
delete ui;
}
//启动按钮对应的槽函数
void Widget::on_start_clicked()
{
quint16 port = quint16(ui->lineEdit->text().toInt());
//判断连接状态
if(isOk == false)
{
//将服务器设置为监听状态
/* bool listen(const QHostAddress &address = QHostAddress::Any,quint16 port = 0)
* 功能:服务器监听客户端的连接
* 参数:
* address:主机地址
* port:端口号
* 返回值:
* true:创建成功
* false:创建失败
*
*/
if(server->listen(QHostAddress::Any,port))
{
qDebug()<<"服务器创建成功";
//设置控件属性
ui->lineEdit->setEnabled(false);
ui->start->setText("关闭服务器");
}
else
{
qDebug()<<"服务器创建失败";
}
//当有新的客户端连接到该服务器后,会发送一个newConnect信号
connect(server,&QTcpServer::newConnection,this,&Widget::on_newConnection_slot);
//更新连接状态
isOk = true;
}
else
{
//主动关闭与客户端所有连接
for(int i=0;i<socketList.count();i++)
{
socketList.at(i)->disconnectFromHost();
socketList[i]->close();
socketList[i] = nullptr;
delete socketList[i];
}
//关闭服务器监听
server->close();
//设置控件属性
ui->lineEdit->setEnabled(true);
ui->start->setText("启动服务器");
//更新连接状态
isOk=false;
}
}
//newConnection对应槽函数的定义
void Widget::on_newConnection_slot()
{
/*QTcpSocket *nextPendingConnection()
* 功能:获取最新连接的客户端的套接字
* 参数:无
* 返回值:新客户端的套接字
*/
QTcpSocket * socket = server->nextPendingConnection();
//将该套接字放入链表中
socketList.append(socket);
//当服务器接受到客户端传过来的消息时,就会触发一个readyRead信号,在该信号对应的槽函数中处理相关逻辑
connect(socket,&QTcpSocket::readyRead,this,&Widget::on_readyRead_slot);
}
//readyRead对应槽函数的定义
void Widget::on_readyRead_slot()
{
//qDebug()<
//判断客户端套接字是否有效,如果无效则剔除
for(int i=0;i<socketList.count();i++)
{
if(socketList.at(i)->state() == false)
{
socketList.removeAt(i);
}
}
//遍历客户端的链表,判断是哪一个客户端发送消息
for(quint16 i=0;i<socketList.count();i++)
{
/*qint64 bytesAvailable()
* 功能:获取客户端中待读数据的个数
* 参数:无
* 返回值:待读数据个数
*/
if(socketList.at(i)->bytesAvailable())
{
/*QByteArray readAll()
* 功能:获取客户端传过来的待读数据
* 参数:无
* 返回值:QByteArray()类型数据
*/
QString mes = QString::fromLocal8Bit(socketList.at(i)->readAll());
//将消息展示到 listwidget 上
ui->listWidget->addItem(mes);
//保持显示最后一行消息
ui->listWidget->scrollToBottom();
//同时还要把该消息进行广播出去
send(mes);
}
}
}
//自定义发送消息函数
void Widget::send(QString mes)
{
for(int i=0;i<socketList.count();i++)
{
socketList.at(i)->write(mes.toLocal8Bit());
}
}
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
//connected信号对应槽函数的声明
void on_connected_slot();
//disconnected信号对应槽函数的声明
void on_disconnected_slot();
//readyRead信号对应槽函数的声明
void on_readyRead_slot();
//按钮连接对应槽函数
void on_connectbtn_clicked();
//按钮发送对应槽函数
void on_sendbtn_clicked();
private:
Ui::Widget *ui;
//定义客户端套接字指针
QTcpSocket *socket;
//定义用户名
QString userName;
//定义bool类型数据,表示连接状态
bool isOk;
};
#endif // WIDGET_H
.cpp文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置标题栏
QWidget::setWindowTitle("TCP服务器");
//实例化socket
socket = new QTcpSocket(this);
//给连接状态初始值
isOk = false;
//将connected信号连接到自定义的槽函数处理相关逻辑
connect(socket,&QTcpSocket::connected,this,&Widget::on_connected_slot);
//将disconnected信号连接到自定义的槽函数处理相关逻辑
connect(socket,&QTcpSocket::disconnected,this,&Widget::on_disconnected_slot);
//当客户端接收到服务器发的消息后,会触发一个readyRead信号,在该信号对应的槽函数中书写逻辑
connect(socket,&QTcpSocket::readyRead,this,&Widget::on_readyRead_slot);
//将单行文本框editingFinished信号连接到larmada表达式处理相关逻辑
connect(ui->send_edit,&QLineEdit::editingFinished,this,[=](){
on_sendbtn_clicked();
});
}
Widget::~Widget()
{
delete ui;
}
//连接服务器对应按钮的槽函数
void Widget::on_connectbtn_clicked()
{
QString ip = ui->ip_edit->text();
quint16 port = quint16(ui->port_edit->text().toInt());
//判断连接状态
if(isOk == false)
{
//连接服务器
/*void connectToHost(const QHostAddress &address,quint16 port,OpenMode mode = ReadWrite)
*功能:客户端连接服务器函数
* 参数:
* address:主机地址
* port:端口号
*/
socket->connectToHost(QHostAddress(ip),port);
//一旦连接成功,就会触发一个connected的信号,在该信号对应的槽函数中处理逻辑
//判断是否连接成功
if (socket->waitForConnected(100))
{
//连接成功
//将按钮文本改为“断开连接”
ui->connectbtn->setText("断开连接");
isOk = true;
}
else
{
//连接失败
//跳出提示弹窗
QMessageBox::information(this,"TCP客户端","连接失败",QMessageBox::Ok);
}
}
else
{
//离开群聊
userName = ui->username_edit->text();
QString mes = userName + "离开群聊";
socket->write(mes.toLocal8Bit());
//断开服务器
/*void disconnectFromHost()
* 功能:断开该客户端的连接
* 参数,返回值:无
*/
socket->disconnectFromHost();
//一旦断开成功,就会触发一个disconnected的信号,在该信号对应的槽函数中处理逻辑
//判断是否断开成功
if(socket->waitForDisconnected(100))
{
//断开成功
//将按钮文本改为“连接服务器”
ui->connectbtn->setText("连接服务器");
isOk = false;
}
else
{
//断开失败
//跳出提示弹窗
QMessageBox::information(this,"TCP客户端","断开失败",QMessageBox::Ok);
}
}
}
//connected信号对应槽函数的定义
void Widget::on_connected_slot()
{
userName = ui->username_edit->text();
QString mes = userName + "加入群聊";
socket->write(mes.toLocal8Bit());
//相关控件置为不可用
ui->username_edit->setEnabled(false);
ui->ip_edit->setEnabled(false);
ui->port_edit->setEnabled(false);
}
//disconnected信号对应槽函数的定义
void Widget::on_disconnected_slot()
{
//相关控件设置
ui->username_edit->setEnabled(true);
ui->ip_edit->setEnabled(true);
ui->port_edit->setEnabled(true);
}
//readyRead信号对应槽函数的定义
void Widget::on_readyRead_slot()
{
//获取套接字中的数据
QString mes = QString::fromLocal8Bit(socket->readAll());
//将数据显示在list widget中
ui->listWidget->addItem(mes);
//保持显示最后一行消息
ui->listWidget->scrollToBottom();
}
//发送按钮对应的槽函数
void Widget::on_sendbtn_clicked()
{
userName = ui->username_edit->text();
QString mes = userName + ":" + ui->send_edit->text();
//将数据发送给服务器
socket->write(mes.toLocal8Bit());
//将文本框清空
ui->send_edit->clear();
}
第二次启动服务器时,客户端连接不上,且会导致服务器程序崩溃