上一篇讲到了如何实现稳定串口通信的几种方式,最后我们引申出使用kcp实现串口稳定通信,这篇文章将着重讲述如何用c++ qt实现,即使不懂c++ qt的同学也不要着急,只要明白其中的原理,用任何语言实现都是可以的。
如果熟悉qt的同学肯定知道一个抽象类,那就是QIODevice,这个类是所有输入输出设备的抽象类,例如QTCPSocket、QFile 、QSerialPort等。
继承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即可,具体原因可以去查询,这里不过多描述。
其实到这里就讲的差不多了,如果有哪些细节不懂可以给我私信,我后续再补充,代码等我整理完成写完单元测试后会上传。