QTcpSocket 如何统计在线时长

  1. 基本原理
    • QTcpSocket是 Qt 库中用于 TCP 通信的类。要统计在线时长,关键思路是记录连接建立的时间和当前时间,通过计算两者的差值来得到在线时长。
  2. 实现步骤
    • 记录连接建立时间
      • 在连接成功的信号槽函数中记录开始时间。例如,当QTcpSocket成功连接到服务器时,会发出connected()信号。可以在对应的槽函数中使用QDateTime类来记录连接时间。
      • 示例代码如下:
       

      收起

      cpp

      #include <QDateTime>
      #include <QTcpSocket>
      // 假设这是你的类,其中包含QTcpSocket对象tcpSocket
      class MyClass
      {
      public:
          MyClass()
          {
              connect(&tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
          }
      private slots:
          void onConnected()
          {
              connectStartTime = QDateTime::currentDateTime();
          }
      private:
          QTcpSocket tcpSocket;
          QDateTime connectStartTime;
      };
      
    • 计算在线时长
      • 需要在适当的时候(比如断开连接或者定期检查)计算在线时长。可以通过获取当前时间QDateTime::currentDateTime(),然
# 题目重述 在已有客户端功能(交易记录管理、通信记录管理、报表生成)的基础上,**完整补充服务端(Server)相关代码**,包括: - `server.h` 和 `server.cpp` 的完整定义 - TCP 服务器监听与连接处理 - 数据库初始化(SQLite) - 处理客户端请求:添加交易、查询交易、添加通信、查询通信等 - 响应格式统一为 JSON --- # 给出答案(完整的 Server 端代码) ### ✅ 1. 头文件:`server.h` ```cpp #ifndef SERVER_H #define SERVER_H #include <QObject> #include <QTcpServer> #include <QSqlDatabase> class QTcpSocket; class Server : public QObject { Q_OBJECT public: explicit Server(QObject *parent = nullptr); ~Server(); private slots: void onNewConnection(); void onReadyRead(); // 处理每个 socket 的数据读取 private: void setupDatabaseSchema(); // 创建数据库表结构 void handleTransactionGet(QTcpSocket *socket, const QJsonObject &request); void handleInsertTransaction(QTcpSocket *socket, const QJsonObject &request); void handleCommGet(QTcpSocket *socket, const QJsonObject &request); void handleInsertComm(QTcpSocket *socket, const QJsonObject &request); // 辅助函数:发送错误响应 void sendError(QTcpSocket *socket, const QString &type, const QString &message); private: QTcpServer *m_server; QSqlDatabase m_db; }; #endif // SERVER_H ``` --- ### ✅ 2. 源文件:`server.cpp` ```cpp #include "server.h" #include <QTcpSocket> #include <QJsonDocument> #include <QJsonObject> #include <QSqlQuery> #include <QSqlError> #include <QDebug> #include <QByteArray> // 自定义宏简化字符串转 QByteArray #define _ba(s) QByteArray(s) Server::Server(QObject *parent) : QObject(parent) , m_server(new QTcpServer(this)) { // 启动 TCP 服务器 if (m_server->listen(QHostAddress::Any, 1234)) { connect(m_server, &QTcpServer::newConnection, this, &Server::onNewConnection); qDebug() << "✅ 服务器启动成功,监听端口:1234"; } else { qCritical() << "❌ 服务器启动失败:" << m_server->errorString(); } // 初始化 SQLite 数据库 m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName("crm.db"); // 数据库存储路径 if (!m_db.open()) { qCritical() << "❌ 无法打开数据库:" << m_db.lastError().text(); return; } setupDatabaseSchema(); // 创建所需的数据表 } void Server::onNewConnection() { while (m_server->hasPendingConnections()) { QTcpSocket *socket = m_server->nextPendingConnection(); // 使用 lambda 捕获当前 socket 并绑定 readyRead 信号 connect(socket, &QTcpSocket::readyRead, [this, socket]() { QByteArray data = socket->readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isNull()) { qWarning() << "⚠️ 收到无效JSON数据:" << data; sendError(socket, "unknown", "Invalid JSON"); return; } QJsonObject request = doc.object(); QString type = request["type"].toString(); if (type == "transactionget") { handleTransactionGet(socket, request); } else if (type == "inserttransaction") { handleInsertTransaction(socket, request); } else if (type == "commget") { handleCommGet(socket, request); } else if (type == "insertcomm") { handleInsertComm(socket, request); } else { QJsonObject response; response["type"] = type; response["status"] = "error"; response["message"] = "Unsupported operation"; socket->write(QJsonDocument(response).toJson()); } }); // 客户端断开时自动释放资源 connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); } } ``` --- ### ✅ 3. 初始化数据库表结构 ```cpp void Server::setupDatabaseSchema() { QSqlQuery query(m_db); bool success = true; // 创建交易记录表 success &= query.exec( "CREATE TABLE IF NOT EXISTS transactions (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "manager TEXT NOT NULL," "customer_name TEXT NOT NULL," "amount REAL NOT NULL CHECK(amount > 0)," "details TEXT NOT NULL," "timestamp TEXT NOT NULL" ")" ); // 创建通信记录表 success &= query.exec( "CREATE TABLE IF NOT EXISTS communications (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "manager TEXT NOT NULL," "customer_name TEXT NOT NULL," "comm_type TEXT NOT NULL," // email / call / meeting "content TEXT NOT NULL," "duration INTEGER DEFAULT 0," // 单位:分钟 "timestamp TEXT NOT NULL" ")" ); // 为常用查询字段创建索引(提升性能) query.exec("CREATE INDEX IF NOT EXISTS idx_trans_customer ON transactions(customer_name)"); query.exec("CREATE INDEX IF NOT EXISTS idx_trans_manager ON transactions(manager)"); query.exec("CREATE INDEX IF NOT EXISTS idx_comm_timestamp ON communications(timestamp)"); if (success) { qDebug() << "📦 数据库表初始化完成(transactions, communications)"; } else { qWarning() << "❗建表过程中出现错误:" << query.lastError().text(); } } ``` --- ### ✅ 4. 处理交易记录:查询与添加 #### 查询交易记录(支持关键词搜索和“重置”) ```cpp void Server::handleTransactionGet(QTcpSocket *socket, const QJsonObject &request) { QString keyword = request["data"].toString().trimmed(); // 空表示获取全部 QSqlQuery query(m_db); QString sql = "SELECT manager, customer_name, amount, details, timestamp " "FROM transactions"; if (!keyword.isEmpty()) { sql += " WHERE manager LIKE ? OR customer_name LIKE ? OR details LIKE ?"; } sql += " ORDER BY timestamp DESC"; // 最新的在前 query.prepare(sql); if (!keyword.isEmpty()) { QString pattern = "%" + keyword + "%"; query.addBindValue(pattern); query.addBindValue(pattern); query.addBindValue(pattern); } if (!query.exec()) { qWarning() << "❌ 查询交易失败:" << query.lastError().text(); sendError(socket, "transactionget", "Database query failed"); return; } QJsonArray dataArray; while (query.next()) { QJsonObject row; row["manager"] = query.value("manager").toString(); row["customer_name"] = query.value("customer_name").toString(); row["amount"] = query.value("amount").toDouble(); row["details"] = query.value("details").toString(); row["timestamp"] = query.value("timestamp").toString(); dataArray.append(row); } QJsonObject response; response["type"] = "transactionget"; response["status"] = "success"; response["data"] = dataArray; socket->write(QJsonDocument(response).toJson()); } ``` #### 添加交易记录 ```cpp void Server::handleInsertTransaction(QTcpSocket *socket, const QJsonObject &request) { QStringList requiredFields = {"manager", "customer_name", "details", "timestamp"}; for (const QString &field : requiredFields) { if (request[field].toString().trimmed().isEmpty()) { goto fail; } } double amount = request["amount"].toDouble(); if (amount <= 0) { goto fail; } QSqlQuery query(m_db); query.prepare("INSERT INTO transactions (manager, customer_name, amount, details, timestamp) " "VALUES (?, ?, ?, ?, ?)"); query.addBindValue(request["manager"].toString()); query.addBindValue(request["customer_name"].toString()); query.addBindValue(amount); query.addBindValue(request["details"].toString()); query.addBindValue(request["timestamp"].toString()); if (!query.exec()) { qWarning() << "❌ 插入交易失败:" << query.lastError().text(); sendError(socket, "inserttransaction", "Save to database failed"); return; } QJsonObject response; response["type"] = "inserttransaction"; response["status"] = "success"; response["message"] = "交易记录添加成功"; socket->write(QJsonDocument(response).toJson()); return; fail: sendError(socket, "inserttransaction", "Missing or invalid parameters"); } ``` --- ### ✅ 5. 处理通信记录:查询与添加 #### 查询通信记录 ```cpp void Server::handleCommGet(QTcpSocket *socket, const QJsonObject &request) { QString keyword = request["data"].toString().trimmed(); QSqlQuery query(m_db); QString sql = "SELECT manager, customer_name, comm_type, content, duration, timestamp " "FROM communications"; if (!keyword.isEmpty()) { sql += " WHERE manager LIKE ? OR customer_name LIKE ? OR content LIKE ?"; } sql += " ORDER BY timestamp DESC"; query.prepare(sql); if (!keyword.isEmpty()) { QString pattern = "%" + keyword + "%"; query.addBindValue(pattern); query.addBindValue(pattern); query.addBindValue(pattern); } if (!query.exec()) { sendError(socket, "commget", "Query failed"); return; } QJsonArray arr; while (query.next()) { QJsonObject row; row["manager"] = query.value(0).toString(); row["customer_name"] = query.value(1).toString(); row["comm_type"] = query.value(2).toString(); row["content"] = query.value(3).toString(); row["duration"] = query.value(4).toInt(); row["timestamp"] = query.value(5).toString(); arr.append(row); } QJsonObject resp; resp["type"] = "commget"; resp["status"] = "success"; resp["data"] = arr; socket->write(QJsonDocument(resp).toJson()); } ``` #### 添加通信记录 ```cpp void Server::handleInsertComm(QTcpSocket *socket, const QJsonObject &request) { QStringList fields = {"manager", "customer_name", "comm_type", "content", "timestamp"}; for (const QString &f : fields) { if (request[f].toString().trimmed().isEmpty()) { sendError(socket, "insertcomm", "Missing required field: " + f); return; } } int duration = request["duration"].toInt(); QSqlQuery query(m_db); query.prepare("INSERT INTO communications (manager, customer_name, comm_type, content, duration, timestamp) " "VALUES (?, ?, ?, ?, ?, ?)"); query.addBindValue(request["manager"]); query.addBindValue(request["customer_name"]); query.addBindValue(request["comm_type"]); query.addBindValue(request["content"]); query.addBindValue(duration); query.addBindValue(request["timestamp"]); if (!query.exec()) { sendError(socket, "insertcomm", "Database error"); return; } QJsonObject resp; resp["type"] = "insertcomm"; resp["status"] = "success"; resp["message"] = "通信记录添加成功"; socket->write(QJsonDocument(resp).toJson()); } ``` --- ### ✅ 6. 辅助函数:统一错误返回 ```cpp void Server::sendError(QTcpSocket *socket, const QString &type, const QString &message) { QJsonObject response; response["type"] = type; response["status"] = "error"; response["message"] = message; socket->write(QJsonDocument(response).toJson()); } ``` --- ## 📦 示例请求与响应 ### 添加交易请求: ```json { "type": "inserttransaction", "manager": "张经理", "customer_name": "李四", "amount": 2999.99, "details": "iPhone 15 Pro", "timestamp": "2025-04-05 14:30:22" } ``` ### 查询通信请求(含“重置”): ```json { "type": "commget", "data": "" } // 重置 → 获取所有 { "type": "commget", "data": "会议" } // 搜索关键字 ``` --- # 代码解析 1. **模块化设计清晰**: - 每个操作独立成函数,职责单一。 - 使用 `sendError()` 统一错误处理。 2. **安全性高**: - 所有 SQL 使用预编译语句防止注入。 - 输入参数严格校验。 3. **高性能**: - 对关键字段建立数据库索引。 - 查询结果按时间倒序排列。 4. **可扩展性强**: - 可轻松新增其他模块(如客户管理、统计图表)。 - 支持未来增加分页、权限控制等功能。 --- # 知识点(列出该代码中遇到的知识点) - **SQLite数据库操作**:使用QSqlQuery执行增删改查语句,结合预编译防止SQL注入。 - **JSON请求响应处理**:解析客户端发来的JSON,并构造结构化响应返回。 - **TCP通信与槽函数绑定**:通过QTcpSocket接收数据,用lambda表达式捕获socket对象进行响应。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值