Qt通过TCP方式连接硬件设备,一般有两种模式:服务器模式和客户端模式。
服务器模式:即硬件设备作为客户端主动连接Qt程序;
优点:能够实时掌握硬件设备的连接状况(如是否断线等),可以更加实时的处理需要进行重连的情况等。
缺点:设备的重连机制为黑盒,依赖设备TCP客户端的实现程序;
需要对设备的要求摸清楚,如有些设备可能会有心跳的反馈信息有详细的要求,如不按照 要求实现会导致掉线或其他状况发生。
客户端模式:即Qt程序作为客户端主动连接硬件设备(硬件设备是服务器);
优点:相比于服务器端,需要实现的功能略少,相对比较适合稳定的生产环境(物理环境、网络环境稳定,仪器设备运行环境稳定,不易出现各种故障)。
缺点:QTcpSocket客户端模式对连接的监控效果不太理想,可能需要重新实现一下检测是否断线并进行断线重连的机制(socket的连接状态码及write返回的效果可能不是真实情况或实时情况)。
Qt6支持了httpserver模式,Qt5不支持,需要基于QTcpSocket进行实现。
Demo:Qt5.15.2
cpp
// TcpDeviceClient.h
#ifndef TCPDEVICECLIENT_H
#define TCPDEVICECLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QTimer>
#include <QHostAddress>
#include "logmanager.h"
class TcpDeviceClient : public QObject
{
Q_OBJECT
public:
explicit TcpDeviceClient(QObject *parent = nullptr);
~TcpDeviceClient();
// 连接设备
bool connectToDevice(const QString &host, quint16 port, int timeoutMs = 5000);
// 断开连接
void disconnectFromDevice();
// 发送数据
qint64 sendData(const QByteArray &data);
// 获取连接状态
bool isConnected() const;
// 设置自动重连
void setAutoReconnect(bool enable, int intervalMs = 5000);
// 获取错误信息
QString errorString() const;
signals:
void connected();
void disconnected();
void dataReceived(const QByteArray &data);
void errorOccurred(const QString &error);
void connectionStatusChanged(bool connected);
public slots:
void reconnect();
private slots:
void onConnected();
void onDisconnected();
void onReadyRead();
void onError(QAbstractSocket::SocketError error);
void onReconnectTimeout();
private:
QTcpSocket *m_socket;
QTimer *m_reconnectTimer;
QString m_host;
quint16 m_port;
bool m_autoReconnect;
int m_reconnectInterval;
bool m_isConnecting;
};
#endif // TCPDEVICECLIENT_H
cpp
// TcpDeviceClient.cpp
#include "tcpdeviceclient.h"
#include <QDebug>
TcpDeviceClient::TcpDeviceClient(QObject *parent)
: QObject(parent)
, m_socket(new QTcpSocket(this))
, m_reconnectTimer(new QTimer(this))
, m_port(0)
, m_autoReconnect(false)
, m_reconnectInterval(5000)
, m_isConnecting(false)
{
//LogManager::initialize();
// 连接信号槽
connect(m_socket, &QTcpSocket::connected, this, &TcpDeviceClient::onConnected);
connect(m_socket, &QTcpSocket::disconnected, this, &TcpDeviceClient::onDisconnected);
connect(m_socket, &QTcpSocket::readyRead, this, &TcpDeviceClient::onReadyRead);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred),
this, &TcpDeviceClient::onError);
// 重连定时器
connect(m_reconnectTimer, &QTimer::timeout, this, &TcpDeviceClient::onReconnectTimeout);
m_reconnectTimer->setSingleShot(true);
}
TcpDeviceClient::~TcpDeviceClient()
{
disconnectFromDevice();
}
bool TcpDeviceClient::connectToDevice(const QString &host, quint16 port, int timeoutMs)
{
qDebug()<<"尝试连接设备:"<<host<<":"<<port;
if (m_isConnecting || isConnected()) {
qDebug()<<"已有连接状态,不能重新连接"<<host<<"::"<<port;
return false;
}
m_host = host;
m_port = port;
qDebug()<<"IP:"<<m_host<<"PORT:"<<m_port;
m_isConnecting = true;
// 连接超时处理
QTimer::singleShot(timeoutMs, this, [this]() {
if (m_isConnecting) {
m_isConnecting = false;
emit errorOccurred("连接超时");
if (m_autoReconnect) {
m_reconnectTimer->start(m_reconnectInterval);
}
}
});
m_socket->connectToHost(host, port);
return true;
}
void TcpDeviceClient::disconnectFromDevice()
{
qDebug()<<"TCPDeviceClient::disconnectFromDevice::";
m_reconnectTimer->stop();
m_isConnecting = false;
// if(m_socket)
// {
// qDebug()<<"tcp device client abort!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11";
// m_socket->abort();
// }
if (m_socket->state() != QAbstractSocket::UnconnectedState) {
m_socket->disconnectFromHost();
if (m_socket->state() != QAbstractSocket::UnconnectedState) {
m_socket->waitForDisconnected(1000);
}
}
}
qint64 TcpDeviceClient::sendData(const QByteArray &data)
{
qDebug()<<"TCPDeviceClient::senddata";
if (!isConnected()) {
qDebug()<<"device is not connected.";
emit errorOccurred("设备未连接");
return -1;
}
qint64 bytesWritten = m_socket->write(data);
if (bytesWritten == -1) {
qDebug()<<"senddata failure.";
emit errorOccurred("发送数据失败: " + m_socket->errorString());
} else if (bytesWritten < data.size()) {
qDebug()<<"senddata uncompleted.";
emit errorOccurred("数据未完全发送");
}
return bytesWritten;
}
bool TcpDeviceClient::isConnected() const
{
return m_socket->state() == QAbstractSocket::ConnectedState;
}
void TcpDeviceClient::setAutoReconnect(bool enable, int intervalMs)
{
m_autoReconnect = enable;
m_reconnectInterval = intervalMs;
if (!enable) {
m_reconnectTimer->stop();
}
}
QString TcpDeviceClient::errorString() const
{
return m_socket->errorString();
}
void TcpDeviceClient::reconnect()
{
qDebug()<<"重新连接"<<m_host<<":"<<m_port;
if (!m_host.isEmpty() && m_port > 0) {
connectToDevice(m_host, m_port);
}
}
void TcpDeviceClient::onConnected()
{
m_isConnecting = false;
m_reconnectTimer->stop();
emit connected();
emit connectionStatusChanged(true);
qDebug() << "TCP设备连接成功:" << m_host << ":" << m_port;
}
void TcpDeviceClient::onDisconnected()
{
emit disconnected();
emit connectionStatusChanged(false);
qDebug() << "TCP设备连接断开";
if (m_autoReconnect) {
m_reconnectTimer->start(m_reconnectInterval);
}
}
void TcpDeviceClient::onReadyRead()
{
QByteArray data = m_socket->readAll();
qDebug()<<"TCP DEVICE CLIENT:data received:";
qDebug()<<QString::fromStdString(data.toStdString());
if (!data.isEmpty()) {
emit dataReceived(data);
}
}
void TcpDeviceClient::onError(QAbstractSocket::SocketError error)
{
m_isConnecting = false;
QString errorMsg;
switch (error) {
case QAbstractSocket::ConnectionRefusedError:
errorMsg = "连接被拒绝";
break;
case QAbstractSocket::RemoteHostClosedError:
errorMsg = "远程主机关闭连接";
break;
case QAbstractSocket::HostNotFoundError:
errorMsg = "主机未找到";
break;
case QAbstractSocket::SocketAccessError:
errorMsg = "套接字访问错误";
break;
case QAbstractSocket::SocketResourceError:
errorMsg = "套接字资源错误";
break;
case QAbstractSocket::SocketTimeoutError:
errorMsg = "套接字超时";
break;
case QAbstractSocket::NetworkError:
errorMsg = "网络错误";
break;
default:
errorMsg = "未知错误: " + m_socket->errorString();
break;
}
emit errorOccurred(errorMsg);
qDebug() << "TCP连接错误:" << errorMsg<<m_host<<":"<<m_port;
if (m_autoReconnect && !m_isConnecting) {
m_reconnectTimer->start(m_reconnectInterval);
}
}
void TcpDeviceClient::onReconnectTimeout()
{
if (m_autoReconnect && !m_host.isEmpty() && m_port > 0) {
qDebug() << "尝试重新连接..." << m_host << ":" << m_port;
connectToDevice(m_host, m_port);
}
}