基于kcp协议的串口通信实现(c++ qt)

178 阅读3分钟

上一篇讲到了如何实现稳定串口通信的几种方式,最后我们引申出使用kcp实现串口稳定通信,这篇文章将着重讲述如何用c++ qt实现,即使不懂c++ qt的同学也不要着急,只要明白其中的原理,用任何语言实现都是可以的。

如果熟悉qt的同学肯定知道一个抽象类,那就是QIODevice,这个类是所有输入输出设备的抽象类,例如QTCPSocketQFileQSerialPort等。

继承QIODevice需要实现三个方法,分别是:

protected:
    //读取数据
    qint64 readData(char *data, qint64 maxlen);
    //读取一行数据
    qint64 readLineData(char *data, qint64 maxlen);
    //写入数据
    qint64 writeData(const char *data, qint64 len);

这三个虚函数可以确保QIODevice的其他方法可以对设备进行读和写,如readAll()、write()等方法。

为什么要介绍这个类,因为我在设计基于KCP协议的串口类时,我想到了两种设计方式,一种是直接继承qt的串口类QSerialPort,这最符合直觉,但这么写之后就基本上没有了扩展性,例如以后我不仅想在串口上使用此协议,还想在UDP上使用此协议,那么我需要再复制一份代码,并将继承的类改为QUdpSocket,这显然是不对的。

这时我们再回头看看QIODevice,我们可不可以写一个类去实现QIODevice,把KCP抽象成一个可以读写的设备,并使用组合的方式将其他设备组合起来?答案是可以的。

class SerialPortDevice : public QIODevice
{
    Q_OBJECT
public:
    explicit SerialPortDevice(QIODevice * ioDevice,QObject *parent = nullptr);
private:
    QIODevice * ioDevice;
    ikcpcb *kcp1;
}

这里我写了一个SerialPortDevice类并继承了QIODevice,同时我将构造函数添加了一个QIODevice指针。

既然设计好了骨架,那么我们该实现具体细节了,这里我们看一下要重写的方法。

class SerialPortDevice : public QIODevice
{
    Q_OBJECT
public:
    explicit SerialPortDevice(QIODevice * ioDevice,QObject *parent = nullptr);
public:
    //初始化KCP
    bool open(OpenMode mode)override;
    //关闭KCP
    void close()override;
    //是否为随机读写
    bool isSequential() const override;
protected:
    //读取数据
    qint64 readData(char *data, qint64 maxlen)override;
    //读取一行数据
    qint64 readLineData(char *data, qint64 maxlen)override;
    //写入数据
    qint64 writeData(const char *data, qint64 len)override;
private:
    QIODevice * ioDevice;
    ikcpcb *kcp1;
}

这是我们需要重写的方法,其中open()、close()、isSequential()这三个方法并不是纯虚函数,可以不重写,但为了设备的开关与KCP的状态保持同步,我们将会在open()方法中初始化KCP对象,在close()方法中析构KCP对象,isSequential()是标记设备是否可以随机读写,我们在这里直接返回false即可,具体原因可以去查询,这里不过多描述。

其实到这里就讲的差不多了,如果有哪些细节不懂可以给我私信,我后续再补充,代码等我整理完成写完单元测试后会上传。