多路双向串口转网口上位机 C++ 源码及 Socket 通信实现
功能概述
完成了多路网口和串口数据转换的功能。可实现串口接收到的数据通过网口发送出去;网口接收到的数据通过串口发送出去。
- 带有附加的发送窗口,可填写指定的 16 进制数据并完成发送。
- 带有接收窗口,可以 16 进制的方式显示数据。
- 具备自动连接功能,可实现主动连接服务器的功能。
- 可保存配置文件,和自动加载上次的配置。
- 可选择 UDP 和 TCP 两种连接方式。
- 通过网口的继承和派生,实现多态功能。
开发环境
- 开发环境:Qt5.10.1
- 依赖库:Qt 自带的 QSerialPort
- 编译要求:请将源码放到纯英文路径下再编译。
- 说明:源代码中包含注释,设计说明文档等。
核心设计
数据转发逻辑
TranWidget 类作为数据转发枢纽,负责串口与网口的交互:
class TranWidget : public QWidget {
Q_OBJECT
public:
explicit TranWidget(QWidget *parent = nullptr);
void forwardData(const QByteArray &data); // 数据转发枢纽
private slots:
void onSerialReadyRead();
void onSocketReadyRead();
private:
QSerialPort *serial;
AbstractNetClient *netClient; // 多态关键点
HexDisplayWidget *display;
};
重点在于 AbstractNetClient 抽象类,TCP 和 UDP 客户端都从这里派生。当用户切换协议时:
void switchProtocol(ProtocolType type) {
delete netClient; // 销毁旧连接
netClient = (type == TCP) ? new TcpClient() : new UdpClient();
connect(netClient, &AbstractNetClient::dataReceived, this, &TranWidget::handleNetData);
}
十六进制处理
十六进制处理是容易踩坑的地方,显式类型转换确保了 0-255 的正确表示:
QString hexConvert(const QByteArray &data) {
QString output;
for (char ch : data) {
output += QString("%1 ").arg(static_cast<quint8>(ch), 2, 16, QLatin1Char('0'));
}
return output.toUpper().trimmed();
}
子功能模块
- 独立的串口网口 Tran 转换控件
- 继承实现的网口类型选择
- 接收发送都是十六进制显示
- 带有配置自动保存功能
- 具备自动连接功能,无需人工干预
- 带有动态的状态指示灯 LED 闪烁

高级特性
自动重连机制
自动重连机制采用了指数退避策略,重试间隔从 1 秒开始,最多累积到 30 秒:
void AutoConnector::startReconnect() {
if (retryCount < 5) {
retryInterval = qMin(retryInterval * 2, 30);
QTimer::singleShot(retryInterval * 1000, this, &AutoConnector::attemptConnect);
} else {
emit connectionFailed(tr("Maximum retries reached"));
}
}
状态指示灯
状态指示灯模拟真实 LED 的渐变效果:
void StatusLED::paintEvent(QPaintEvent*) {
QPainter painter(this);
QRadialGradient grad(rect().center(), width()/2);
grad.setColorAt(0, currentColor.lighter(150));
grad.setColorAt(1, currentColor.darker(200));
painter.setBrush(grad);
painter.drawEllipse(rect());
}
配置文件处理
配置文件处理使用了 INI 格式,并实现了序列化重载:
void saveWindowState() {
QSettings cfg("config.ini", QIniFormat);
cfg.setValue("MainWindow/geometry", saveGeometry());
cfg.setValue("Connections", activeConnections); // 存储对象
}

应用场景
遇到过一个实际案例:某工业设备每次上电后需要在 5 秒内建立连接。通过配置自动连接参数,设置重试间隔为 500 毫秒,完美解决了手动操作来不及的问题。这种灵活的重试策略在关键场景特别实用。
编译注意事项
最后提醒编译注意事项:千万别用中文路径!Qt 的元对象编译器(moc)处理宽字符路径时偶尔会异常,见过有人折腾两天才发现是路径里有中文括号,这坑实在不值得踩。


