Qt TCP 服务器开发实战:与 VisionMaster OCR 通信

一、项目概述

本文介绍如何使用 Qt 框架开发一个 TCP 服务器,实现与 VisionMaster(VM)视觉检测软件的通信。服务器负责:

  1. 启动 VisionMaster 进程
  2. 监听 TCP 端口等待 VM 客户端连接
  3. 发送触发信号给 VM
  4. 接收并处理 VM 返回的 OCR 识别结果

二、环境准备

工具 版本 说明
Qt 6.11.1 C++ GUI 框架
MinGW 13.1.0 编译器
CMake 3.30+ 构建工具
VisionMaster - 视觉检测软件

三、项目结构

复制代码
VM_test/
├── TcpServer.h          # TCP服务器头文件
├── TcpServer.cpp        # TCP服务器实现
├── main.cpp             # 主入口文件
├── CMakeLists.txt       # CMake配置
├── build.bat            # 构建脚本
└── run.bat              # 运行脚本

四、核心代码解析

4.1 TcpServer.h - 类定义

cpp 复制代码
class TcpServer : public QTcpServer
{
    Q_OBJECT

public:
    bool startServer(quint16 port);    // 启动服务器
    bool startOcrProcess();             // 启动OCR进程
    bool sendData(const QString& data); // 发送数据
    bool isClientConnected() const;     // 检查连接状态

signals:
    void clientConnected();             // 客户端连接信号
    void dataReceived(const QByteArray& data); // 数据接收信号

private slots:
    void onNewConnection();             // 处理新连接
    void onReadyRead();                 // 处理数据接收
    void onDisconnected();              // 处理断开连接

private:
    QTcpSocket* m_clientSocket;         // 客户端socket
    QProcess* m_ocrProcess;             // OCR进程
    QByteArray m_receivedData;          // 接收的数据
};

4.2 TcpServer.cpp - 关键实现

启动服务器

cpp 复制代码
bool TcpServer::startServer(quint16 port)
{
    if (!listen(QHostAddress::Any, port)) {
        qCritical() << "Server start failed:" << errorString();
        return false;
    }
    qInfo() << "Server listening on port" << port;
    return true;
}

listen() 开始在指定端口监听,QHostAddress::Any 表示监听所有网络接口。

处理新连接

cpp 复制代码
void TcpServer::onNewConnection()
{
    QTcpSocket* socket = nextPendingConnection();
    if (!socket) return;

    if (m_clientSocket) {
        socket->close();
        socket->deleteLater();
        return;
    }

    m_clientSocket = socket;

    connect(m_clientSocket, &QTcpSocket::readyRead, 
            this, &TcpServer::onReadyRead);
    connect(m_clientSocket, &QTcpSocket::disconnected, 
            this, &TcpServer::onDisconnected);

    qInfo() << "Client connected:" << socket->peerAddress().toString();
    emit clientConnected();
}

采用单客户端模式,已有连接时拒绝新连接。

接收数据

cpp 复制代码
void TcpServer::onReadyRead()
{
    QByteArray data = m_clientSocket->readAll();
    m_receivedData = data;
    emit dataReceived(data);
    qInfo() << "Received data:" << data.size() << "bytes";
}

readAll() 读取所有可用数据,返回 QByteArray 类型。

发送数据

cpp 复制代码
bool TcpServer::sendData(const QString& data)
{
    if (!m_clientSocket) {
        qWarning() << "No client connected";
        return false;
    }
    m_clientSocket->write(data.toUtf8());
    m_clientSocket->flush();
    return true;
}

字符串通过 toUtf8() 转换为字节数组发送。

4.3 main.cpp - 主流程

cpp 复制代码
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    TcpServer server;

    // 连接成功时打印提示
    QObject::connect(&server, &TcpServer::clientConnected, []() {
        qInfo() << "Client connected";
    });

    // 接收数据时打印结果
    QObject::connect(&server, &TcpServer::dataReceived, [](const QByteArray& data) {
        qInfo() << "Received:" << QString::fromUtf8(data);
    });

    // 启动OCR进程
    server.startOcrProcess();

    // 启动TCP服务器
    server.startServer(7920);

    // 15秒后发送触发信号
    QTimer::singleShot(15000, [&server]() {
        if (server.isClientConnected()) {
            server.sendData("1");
        }
    });

    return a.exec();
}

五、工作流程图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    main.cpp 主流程                          │
├─────────────────────────────────────────────────────────────┤
│  启动程序                                                   │
│      ↓                                                      │
│  创建 TcpServer 对象                                         │
│      ↓                                                      │
│  连接信号槽(clientConnected, dataReceived)                  │
│      ↓                                                      │
│  startOcrProcess() → 启动 VisionMaster.exe                  │
│      ↓                                                      │
│  startServer(7920) → 监听端口7920                           │
│      ↓                                                      │
│  QTimer::singleShot(15000) → 15秒后触发                     │
│      ↓                                                      │
│  a.exec() → 进入事件循环                                     │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                   TcpServer 事件处理                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  [新连接]  onNewConnection()                                 │
│      ├─>  获取 socket                                       │
│      ├─>  连接信号槽(readyRead, disconnected)              │
│      └─>  emit clientConnected()                            │
│                                                             │
│  [数据到达] onReadyRead()                                    │
│      ├─>  readAll() → QByteArray                            │
│      └─>  emit dataReceived(data)                           │
│                                                             │
│  [断开连接] onDisconnected()                                 │
│      └─>  释放 socket,m_clientSocket = nullptr             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

六、通信时序

复制代码
时间轴 →
        │
        │  服务器启动,监听7920端口
        │      ↓
        │  VM客户端连接 → 服务器打印 "Client connected"
        │      ↓
        │  等待15秒...
        │      ↓
        │  服务器发送 "1" → VM开始OCR识别
        │      ↓
        │  VM返回OCR结果 → 服务器打印 "Received: ..."
        │

七、构建与运行

7.1 构建

cmd 复制代码
cd VM_test
build.bat

7.2 运行

cmd 复制代码
cd VM_test\build
ocr_tcp_server.exe

7.3 预期输出

复制代码
OCR TCP Server started
OCR process started. PID: 12345
Server listening on port 7920
Waiting for VM client...
Client connected: 127.0.0.1:54321
Client connected
Triggering OCR...
Sent: 1
Received: {"result":"识别成功","text":"Hello World"}

八、技术要点总结

要点 说明
信号槽机制 Qt 核心机制,实现对象间松耦合通信
单客户端模式 同一时间只允许一个客户端连接
事件循环 a.exec() 启动 Qt 事件循环,处理所有事件
数据转换 QString ↔ QByteArray 使用 UTF-8 编码
进程管理 使用 QProcess 启动外部程序

九、常见问题

  1. 连接不上:检查端口是否被占用、防火墙是否拦截
  2. 乱码 :确认编码格式,尝试 fromLocal8Bit() 替代 fromUtf8()
  3. VM不返回数据:检查 VM 的 TCP 配置和数据输出设置

这篇博客涵盖了从项目架构到核心代码,再到构建运行的完整流程。

相关推荐
浅念-3 小时前
Redis基础详解:单线程模型、String与Hash数据结构
服务器·数据库·redis·sql·mysql·nosql数据库·nosql
Shadow(⊙o⊙)4 小时前
Linux进阶知识1.0
linux·运维·服务器
剑指offer.5 小时前
ARM裸机开发-点亮LED灯
linux·服务器·嵌入式硬件
Wang's Blog5 小时前
Java 接入Redis: 使用Jedis操作Redis
java·服务器·redis
Ivanqhz6 小时前
数据搬运是性能关键
java·服务器·网络·人工智能·深度学习
其实防守也摸鱼6 小时前
常见安全架构中 Shiro 的认证确认机制解析
运维·服务器·数据库·windows·github
Julien20046 小时前
自动挂载网络附加存储
linux·服务器·网络
Ivanqhz7 小时前
Slope One 算法详解:与矩阵分解、共现矩阵的异同
java·服务器·网络·人工智能·深度学习
Wang's Blog8 小时前
Java 项目实战: 外卖平台-数据库环境搭建与十一张表结构解析
java·服务器·redis
王六米。8 小时前
RAG知识库建设 文档解析 切分与索引如何衔接
服务器·前端·网络·企业数字化转型·智钳智能盒子