手动开发一个TCP服务器调试工具(四):构建完整的UI与功能联合的TCP服务器应用
在现代应用中,TCP服务器通常用于实现可靠的数据传输,而在需要用户交互的场景下,结合图形用户界面(UI)进行操作会大大提升用户体验。本文将结合一个使用Qt与C++构建的TCP服务器应用,介绍如何将UI和TCP服务器的功能有效结合,实现一个完整的功能应用。
1. 系统设计概述
我们设计的TCP服务器应用主要由两个部分组成:
- TCP服务器模块(TcpServer):负责客户端的连接管理、消息的发送与接收。
- UI模块(MainWindow):通过Qt提供的图形界面,展示服务器的状态信息、客户端列表,并允许用户进行数据发送和操作。
在这个设计中,TCP服务器负责处理底层的连接、数据传输以及错误和警告的管理,而UI则为用户提供了简便的操作界面。
源码下载
通过网盘分享的文件:完整的UI与功能联合的TCP服务器应用
链接: https://pan.baidu.com/s/1aWIAPyW7q4dMZVcnhzfD0A?pwd=jkcf 提取码: jkcf
2. TCP服务器模块
2.1 TCP服务器类设计(TcpServer)
TcpServer
类是服务器的核心,继承自QThread
,利用线程独立运行以避免阻塞主UI线程。以下是主要功能实现:
启动与关闭服务器
bool TcpServer::startServer(int port)
{if(!tcpServer){tcpServer = new QTcpServer();connect(tcpServer, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);}if (!tcpServer->listen(QHostAddress::Any, static_cast<quint16>(port))){emit errors(1, tc("服务器启动失败:%1 端口: ").arg(tcpServer->errorString()) + QString::number(port));return false;}emit informations(1, tc("服务器启动成功,监听端口: ") + QString::number(port));return true;
}
startServer(int port)
:启动服务器并监听指定端口。如果服务器启动失败,会通过emit
发送错误信号。closeServer()
:关闭服务器并断开所有客户端的连接。
客户端连接管理
当有新客户端连接时,onNewConnection()
方法会被触发,建立与客户端的连接并保存到clients
列表中。
void TcpServer::onNewConnection()
{QTcpSocket *clientSocket = tcpServer->nextPendingConnection();qintptr socketDescriptor = clientSocket->socketDescriptor();clients.insert(socketDescriptor, clientSocket); // 将客户端添加到列表中connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead);emit clientConnected(socketDescriptor); // 触发客户端连接信号
}
- 每当有客户端连接时,客户端的
socketDescriptor
和对应的QTcpSocket
实例会被保存到clients
映射表中。
数据接收与发送
onReadyRead()
用于接收客户端发送的数据,而sendToClient()
则用于向客户端发送消息。
void TcpServer::onReadyRead()
{QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());if (clientSocket) {QByteArray data = clientSocket->readAll();emit ClientInfor(clientSocket->socketDescriptor(), data, true); // 触发数据接收信号}
}void TcpServer::sendToClient(qintptr socketDescriptor, const QByteArray &message)
{QTcpSocket *clientSocket = clients.value(socketDescriptor, nullptr);if (clientSocket){clientSocket->write(message);emit ClientInfor(clientSocket->socketDescriptor(), message, false); // 触发数据发送信号} else {emit errors(2, tc("无法找到指定的客户端,描述符: ") + QString::number(socketDescriptor));}
}
- 当接收到数据时,
onReadyRead()
读取客户端的数据并通过信号ClientInfor
通知UI。 sendToClient()
会将消息写入指定的客户端连接中。
3. UI设计与实现
3.1 主窗口(MainWindow)设计
MainWindow
类是应用的UI核心,使用Qt的QWidget
及各种控件(如按钮、文本框等)构建了直观的操作界面。UI主要功能包括:
启动与关闭服务器
通过点击按钮来启动和关闭TCP服务器。点击启动时,会触发on_startServerButton_clicked()
方法,验证端口号并调用startServer()
启动服务器。
void MainWindow::on_startServerButton_clicked()
{int port = ui->portspinBox->value();if (port > 0) {if (server->startServer(port)) {writeRunTimeMsgs(QString("服务器成功启动,监听端口:") + QString::number(port), 2);ui->startServerButton->setEnabled(false); // 禁用启动按钮} else {writeRunTimeMsgs(QString("服务器启动失败!"), 0);}} else {writeRunTimeMsgs(QString("无效的端口号!"), 1);}
}
- 如果服务器成功启动,UI会显示成功信息,并禁用启动按钮。
客户端连接显示
通过onClientConnected()
和onClientDisconnected()
,分别处理客户端连接和断开连接事件,并将客户端的描述符显示在UI列表中。
void MainWindow::onClientConnected(qintptr socketDescriptor)
{writeRunTimeMsgs(QString("客户端连接,描述符: ") + QString::number(socketDescriptor), 2);QStandardItem *item = new QStandardItem(QString("客户端#%1").arg(socketDescriptor));item->setData(socketDescriptor, Qt::UserRole);model->appendRow(item); // 将客户端信息添加到客户端列表
}
数据发送
MainWindow
通过TimeSendWidget
组件来创建数据发送框,允许用户选择客户端并输入数据发送。
void MainWindow::createSendLinEdit()
{for (int i = 0; i < 5; i++) {TimeSendWidget *sendWidget = new TimeSendWidget;sendWidget->setComBoxModel(model); // 设置客户端列表connect(sendWidget, &TimeSendWidget::sendLineData, [=](const qintptr socketDescriptor, const QByteArray &data) {if (data.isEmpty()) {writeRunTimeMsgs(QString("信息为空,拒绝发送"), Warning);return;}((socketDescriptor < 0) ? server->sendToAllClients(data) : server->sendToClient(socketDescriptor, data));});ui->verticalLayout->addWidget(sendWidget); // 将发送框添加到UI布局中}
}
- 用户可以选择客户端并输入数据,点击发送按钮时,数据将通过
sendToClient()
或sendToAllClients()
发送。
3.2 消息显示
UI通过QTextEdit
显示运行时消息以及接收到的数据:
void MainWindow::writeRunTimeMsgs(const QString &msg, const int level)
{QString prefix;QString color;// 获取当前时间QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");switch (level) {case 0: // 错误prefix = QString("【错误】");color = "red";break;case 1: // 警告prefix = QString("【警告】");color = "orange";break;case 2: // 信息prefix = QString("【信息】");color = "#00ff00";break;}// 将消息插入到QTextEdit中并改变颜色QString formattedMsg = QString("<span style='color:%1;'>%2 %3: %4</span>").arg(color, prefix, timestamp, msg);ui->outputTextEdit->append(formattedMsg);
}
- 根据不同级别的消息(错误、警告、信息),UI会显示不同颜色的提示信息。
4. 总结
本应用通过Qt与C++结合,构建了一个完整的TCP服务器应用,既提供了功能强大的服务器端数据管理,又通过直观的UI设计,便于用户操作。服务器支持多客户端连接、群发与单独发送数据,并能实时处理错误和信息,确保了应用的高效和稳定运行。通过本系统,用户可以轻松管理连接的客户端,进行数据交互,且在服务器端出现任何问题时可以即时反馈。