目录
自定义一个继承与复选框类的类,重写鼠标点击事件,定义自定义信号
当 QTcpSocket 的连接状态发生变化时,stateChanged信号会被发出 参数 socketState当前的套接字状态
当点击通信协议和服务器ip地址的复选框值会发送信号触发mComboBox_refresh函数
客户端
步骤
ui界面配置![](https://img-blog.csdnimg.cn/direct/375f9e0283bb4f44a17811008fc60cd8.png)
添加头函数,类成员数据,类成员函数
#include <QTcpSocket>
#include <QWidget>
private slots:
//连接按钮
void on_btnConnect_clicked();
//收到来自服务器的数据触发
void mRead_Data_From_Server();
//发送按钮
void on_btnSend_clicked();
//断开按钮
void on_btndiscon_clicked();
private:
QTcpSocket *client;//tcp描述符
//向textEditRev文本编辑器中插入指定颜色的文本
void mInserTextByColor(Qt::GlobalColor color,QString str);
添加模块
QT += core gui network
构造函数
//断开按钮和发送按钮关闭
ui->btndiscon->setEnabled(false);
ui->btnSend->setEnabled(false);
//创建新的QTcpSocket对象
client = new QTcpSocket(this);
//当client对象的数据可读(收到来自服务器的数据)时,readyRead()信号会被发出,
//然后mRead_Data_From_Server()槽函数会被调用以处理接收到的数据
connect(client,SIGNAL(readyRead()),this,SLOT(mRead_Data_From_Server()));
连接按钮
//连接按钮
void Widget::on_btnConnect_clicked()
{
// 尝试连接到指定的IP地址和端口
client->connectToHost(ui->lineEditIPAddr->text(),ui->lineEditPort->text().toInt());
// 检查连接状态
if(client->state() == QAbstractSocket::ConnectedState ||
client->state()==QAbstractSocket::ConnectingState){
// 如果连接成功或正在连接中,执行以下操作
ui->textEditRev->append("连接成功!"); // 在文本编辑器中添加“连接成功!”的消息
ui->btnConnect->setEnabled(false); // 禁用“连接”按钮,因为它已经完成了连接操作
ui->lineEditPort->setEnabled(false); // 禁用端口输入框,因为连接已经建立
ui->lineEditIPAddr->setEnabled(false); // 禁用IP地址输入框,因为连接已经建立
ui->btndiscon->setEnabled(true); // 启用“断开”按钮,因为现在可以断开连接了
ui->btnSend->setEnabled(true); // 启用“发送”按钮,因为现在可以发送数据了
}
}
收到来自服务器的数据触发
//收到来自服务器的数据触发
void Widget::mRead_Data_From_Server()
{
// 将文本编辑器的光标移动到末尾
ui->textEditRev->moveCursor(QTextCursor::End);
// 确保光标是可见的
ui->textEditRev->ensureCursorVisible();
// 调用mInserTextByColor函数,将接收到的数据以黑色插入到文本编辑器中
mInserTextByColor(Qt::black,client->readAll());
}
发送按钮
//发送按钮
void Widget::on_btnSend_clicked()
{
// 获取文本编辑器textEditSend中的纯文本内容,并将其转换为UTF-8编码的字节数组
QByteArray sendData = ui->textEditSend->toPlainText().toUtf8();
// 将sendData写入到client对象中,用于发送数据到服务器
client->write(sendData);
// 调用mInserTextByColor函数,将刚刚发送的数据以红色插入到textEditRev文本编辑器中
mInserTextByColor(Qt::red,sendData);
}
断开按钮
//断开按钮
void Widget::on_btndiscon_clicked()
{
// 断开与服务器的连接
client->disconnectFromHost();
// 关闭与服务器的连接
client->close();
// 在文本编辑器textEditRev中追加文本“断开连接!
ui->textEditRev->append("断开连接!");
// 启用连接按钮,允许用户重新建立连接
ui->btnConnect->setEnabled(true);
// 启用端口输入框,允许用户输入端口号
ui->lineEditPort->setEnabled(true);
// 启用IP地址输入框,允许用户输入IP地址
ui->lineEditIPAddr->setEnabled(true);
// 禁用断开连接按钮,因为它已经处于断开状态
ui->btndiscon->setEnabled(false);
// 禁用发送按钮,因为当前没有与服务器的连接
ui->btnSend->setEnabled(false);
}
向textEditRev文本编辑器中插入指定颜色的文本
//向textEditRev文本编辑器中插入指定颜色的文本
void Widget::mInserTextByColor(Qt::GlobalColor color,QString str)
{
// 获取文本编辑器的当前文本光标
QTextCursor cursor = ui->textEditRev->textCursor();
// 创建一个文本字符格式对象
QTextCharFormat format;
// 设置字符格式的前景色为指定的颜色
format.setForeground(QBrush(QColor(color)));
// 应用字符格式到文本光标
cursor.setCharFormat(format);
// 在光标位置插入指定的字符串
cursor.insertText(str);
}
服务端
步骤
ui界面配置
添加头函数,类成员数据,类成员函数
#include <QMessageBox>
#include <QNetworkInterface>
#include <QTcpSocket>
#include <QTextCodec>
#include <QWidget>
#include <QTcpServer>
#include "mycombobox.h"
public:
QTcpServer* server;//tcp描述符
public slots:
void on_newClient_connect();//新的TCP连接请求时触发
void on_readyRead_handler();//新的数据接收时触发
void mdisconnected();//当连接的状态断掉时触发
//当 QTcpSocket 的连接状态发生变化时,stateChanged信号会被发出 参数 socketState当前的套接字状态
void mstateChanged(QAbstractSocket::SocketState socketState);
//当点击通信协议和服务器ip地址的复选框值会发送信号触发mComboBox_refresh函数
void mComboBox_refresh();
添加模块
QT += core gui network
自定义一个继承与复选框类的类,重写鼠标点击事件,定义自定义信号
#ifndef MYCOMBOBOX_H
#define MYCOMBOBOX_H
#include <QComboBox>
#include <QWidget>
class MyComboBox : public QComboBox
{
Q_OBJECT
public:
MyComboBox(QWidget *parent);
protected:
void mousePressEvent(QMouseEvent *e) override;
signals:
void on_ComboBox_clicked();
};
#endif // MYCOMBOBOX_H
#include "mycombobox.h"
#include <QMouseEvent>
MyComboBox::MyComboBox(QWidget *parent) : QComboBox(parent)
{
}
void MyComboBox::mousePressEvent(QMouseEvent *e)
{
if(e->button() == Qt::LeftButton){
emit on_ComboBox_clicked();
}
QComboBox::mousePressEvent(e);
}
构造函数
//创建了一个新的QTcpServer对象
server = new QTcpServer(this);
//当点击通信协议和服务器ip地址的复选框值会发送信号触发mComboBox_refresh函数
connect(ui->comboBoxChildren,&MyComboBox::on_ComboBox_clicked,this,&Widget::mComboBox_refresh);
//当server对象的newConnection信号被触发时(新的TCP连接请求)on_newClient_connect槽函数会被调用
connect(server,SIGNAL(newConnection()),this,SLOT(on_newClient_connect()));
//禁用了三个按钮
ui->btnLineout->setEnabled(false);
ui->btnStopListen->setEnabled(false);
ui->btnSend->setEnabled(false);
//获取了所有网络接口的地址,并将它们存储在一个QList<QHostAddress>类型的变量addresss中
QList<QHostAddress> addresss = QNetworkInterface::allAddresses();
//遍历了addresss中的所有地址
for(QHostAddress tmp : addresss){
//检查该地址是否是一个IPv4地址
if(tmp.protocol() == QAbstractSocket::IPv4Protocol){
//将该地址的字符串表示添加到comboBoxAddr组合框中
ui->comboBoxAddr->addItem(tmp.toString());
}
}
新的TCP连接请求时触发
//新的TCP连接请求时触发
void Widget::on_newClient_connect()
{
// 检查是否有待处理的连接
if(server->hasPendingConnections()){
// 获取下一个待处理的连接
QTcpSocket *connction = server->nextPendingConnection();
// 输出客户端的地址和端口到调试信息
qDebug() << "client Addr: " << connction->peerAddress().toString() << "port:" << connction->peerPort();
// 在文本编辑器中插入客户端的地址和端口信息
ui->textEditRev->insertPlainText("客户端地址:"+connction->peerAddress().toString()+
"客户端端口号:"+QString::number(connction->peerPort()) +"\n");//::xxx:192.168.1.9
// 当连接准备好读取数据时,调用on_readyRead_handler槽函数
connect(connction,SIGNAL(readyRead()),this,SLOT(on_readyRead_handler()));
// 当连接的状态改变时,调用mstateChanged槽函数并传递新的状态
connect(connction,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(mstateChanged(QAbstractSocket::SocketState)));
// 将客户端的端口号添加到组合框中,并设置为当前选中的文本
ui->comboBoxChildren->addItem(QString::number(connction->peerPort()));
ui->comboBoxChildren->setCurrentText(QString::number(connction->peerPort()));
// 如果发送按钮当前是禁用的,则启用它
if(!ui->btnSend->isEnabled()){
ui->btnSend->setEnabled(true);
}
}
}
新的数据接收时触发
//新的数据接收时触发
void Widget::on_readyRead_handler()
{
// sender() 函数返回发出当前信号的对象的指针
// 使用 qobject_cast 将发送者转换为 QTcpSocket 指针
QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
// 读取所有可用的数据
QByteArray revData = tmpSock->readAll();
// 将文本编辑器的光标移动到末尾
ui->textEditRev->moveCursor(QTextCursor::End);
// 确保光标是可见的
ui->textEditRev->ensureCursorVisible();
// 在文本编辑器中插入新接收到的数据,前面带有 "客户端: " 的标签
ui->textEditRev->insertPlainText("客户端: " + revData);
}
当连接的状态断掉时触发
//当连接的状态断掉时触发
void Widget::mdisconnected()
{
// 使用 qobject_cast 将发送者转换为 QTcpSocket 指针
QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
// 在调试输出中打印 "client out!"
qDebug() << "client out!";
// 在文本编辑器中插入文本,表明客户端已断开连接
ui->textEditRev->insertPlainText("客户端断开!");
// 计划在事件循环的末尾删除这个 QTcpSocket 对象
tmpSock->deleteLater();
}
当 QTcpSocket 的连接状态发生变化时,stateChanged信号会被发出 参数 socketState当前的套接字状态
//当 QTcpSocket 的连接状态发生变化时,stateChanged信号会被发出 参数 socketState当前的套接字状态
void Widget::mstateChanged(QAbstractSocket::SocketState socketState)
{
//用于存储组合框中对应端口号的索引
int tmpIndex;
//将发出信号的对象转换为 QTcpSocket 指针
QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
//使用 qDebug() 打印当前套接字的状态
qDebug() << "client out In state:" << socketState;
//根据传入的套接字状态 socketState 进行条件判断
switch(socketState){
//当套接字处于未连接状态时 表示客户端已断开连接
case QAbstractSocket::UnconnectedState:
ui->textEditRev->insertPlainText("客户端断开!");
// 从组合框中移除对应的端口号项
tmpIndex = ui->comboBoxChildren->findText(QString::number(tmpSock->peerPort()));
ui->comboBoxChildren->removeItem(tmpIndex);
// 删除 QTcpSocket 对象
tmpSock->deleteLater();
// 检查组合框中是否还有该项
if(ui->comboBoxChildren->count() == 0)
// 禁用发送按钮
ui->btnSend->setEnabled(false);
break;
//当套接字处于已连接 表示有新的客户端接入
case QAbstractSocket::ConnectedState:
//当套接字处于正在连接状态时 表示有新的客户端接入
case QAbstractSocket::ConnectingState:
ui->textEditRev->insertPlainText("客户端接入!");
break;
}
}
当点击通信协议和服务器ip地址的复选框值会发送信号触发mComboBox_refresh函数
//当点击通信协议和服务器ip地址的复选框值会发送信号触发mComboBox_refresh函数
void Widget::mComboBox_refresh()
{
// 清除组合框中的所有项
ui->comboBoxChildren->clear();
// 使用 findChildren 方法获取服务器对象中所有的 QTcpSocket 子对象
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
// 遍历所有找到的 QTcpSocket 对象
for(QTcpSocket* tmp:tcpSocketClients){
// 检查对象是否为空
if(tmp!=nullptr)
// 如果对象不为空,将其对应的端口号添加到组合框中
ui->comboBoxChildren->addItem(QString::number(tmp->peerPort()));
}
// 在组合框中添加 "all" 项
ui->comboBoxChildren->addItem("all");
}
监听按钮
//监听按钮
void Widget::on_btnListen_clicked()
{
// 从界面上的lineEditPort控件获取用户输入的端口号,并将其转换为整数
int port = ui->lineEditPort->text().toInt();
// 尝试在指定的地址和端口上启动服务器监听
if(!server->listen(QHostAddress(ui->comboBoxAddr->currentText()),port)){
qDebug() << "listenError";
QMessageBox msgBox;// 创建一个消息框对象
msgBox.setWindowTitle("监听失败"); // 设置消息框的标题
msgBox.setText("端口号被占用"); // 设置消息框的文本内容
msgBox.exec(); // 显示消息框
return;
}
// 如果监听成功,则禁用“开始监听”按钮,启用“停止监听”和“断开”按钮
ui->btnListen->setEnabled(false);
ui->btnLineout->setEnabled(true);
ui->btnStopListen->setEnabled(true);
}
发送按钮
//发送按钮
void Widget::on_btnSend_clicked()
{
// 获取所有已连接的QTcpSocket对象
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
// 如果没有已连接的客户端
if(tcpSocketClients.isEmpty()){
// 显示一个错误消息框,告知用户当前没有连接
QMessageBox msgBox;
msgBox.setWindowTitle("发送错误!");
msgBox.setText("当前无连接!");
msgBox.exec();
// 禁用发送按钮,避免用户重复点击
ui->btnSend->setEnabled(false);
return;
}
//当用户不选择向all,所有客户端进行发送的时候
if(ui->comboBoxChildren->currentText() != "all"){
// 如果用户没有选择"all",则获取当前选中的客户端名称
QString currentName = ui->comboBoxChildren->currentText();
// 遍历所有已连接的客户端
for(QTcpSocket* tmp : tcpSocketClients){
// 检查当前客户端的端口号是否与选中的名称匹配
if(QString::number(tmp->peerPort()) == currentName){
// 如果匹配,则向该客户端发送数据
tmp->write(ui->textEditSend->toPlainText().toStdString().c_str());
}
}
}else{
//遍历所有子客户端,并一一调用write函数,向所有客户端发送
//遍历所有已连接的客户端
for(QTcpSocket* tmp:tcpSocketClients){
// 将要发送的文本转换为本地8位编码的QByteArray
QByteArray sendData = ui->textEditSend->toPlainText().toLocal8Bit();
// 向每个客户端发送数据
tmp->write(sendData);
}
}
}
停止监听按钮
//停止监听按钮
void Widget::on_btnStopListen_clicked()
{
// 获取所有与server关联的QTcpSocket对象
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
// 遍历所有客户端套接字,并关闭它们
for(QTcpSocket* tmp:tcpSocketClients){
tmp->close();
}
// 关闭服务器
server->close();
// 启用“监听”按钮,使其可以再次被点击
ui->btnListen->setEnabled(true);
ui->btnLineout->setEnabled(false);
ui->btnStopListen->setEnabled(false);
}
断开按钮
//断开按钮
void Widget::on_btnLineout_clicked()
{
// 停止监听按钮
on_btnStopListen_clicked();
// 释放服务器内存
delete server;
// 关闭当前窗口
this->close();
}