3 实现
3.1 Kermit
该模块是Kermit协议实现类。
3.1.1 Kermit定义
/*|<------Included in CHECK----->|| |+------+-----+-----+------+------+- - -+-------+| MARK | LEN | SEQ | TYPE | DATA | CHECK |<terminator>+------+-----+-----+------+------+- - -+-------+| ||<--LEN-32 characters-->|MARK A real control character, usually CTRL-A.LEN One character, length of remainder of packet + 32, max 95SEQ One character, packet sequence number + 32, modulo 64TYPE One character, an uppercase letterCHECK One, two, or three characters, as negotiated.<terminator> Any control character required for reading the packet.
*/class Kermit
{
public:Kermit();enum Code {NUL = 0x00,MARK = 0x01,END_CHAR = 0x0D};enum Type {S = 0x53, //Send Initiation. I’m about to send files, and here are my parameters.F = 0x46, //File Header, the name of the file which is about to come.D = 0x44, //File DataZ = 0x5A, //End of File.B = 0x42, //Break Transmission, end of transaction.Y = 0x59, //AcknowledgmentN = 0x4E, //Negative AcknowledgmentE = 0x45 //Fatal Error};enum Size {MinLen = 3,MaxLen = 94,MaxSize = MaxLen + 6};enum State {SSNUL = 0,SSINT = 1,SSFIL = 2,SSDAT = 3,SSEND = 4,SSBRK = 5};protected:virtual void on_init(int seq, const char* data, int size);virtual void on_file_header(int seq, const char* data, int size);virtual void on_data(int seq, const char* data, int size);virtual void on_end(int seq, const char* data, int size);virtual void on_break(int seq, const char* data, int size);virtual void on_ack(int seq, const char* data, int size);virtual void on_nack(int seq, const char* data, int size);virtual void on_error(int seq, const char* data, int size);virtual int write(char const *data, int size) = 0;virtual int read(char *data, int size) = 0;virtual char getc() = 0;protected:void send_init();void send_data(int n, const char* data, int len);void send_end(int n);void send_break(int n);void send_ack(int n);void send_nack(int n);bool recv_packet();void resend();int encode(char a, char* data);int decode(const char* data, char& a);
private:int tochar(int x) { return x + 32; }int unchar(int x) { return int(x - 32); }int ctl(int x) { return x^64; }int check(const char* p);int check(int sum, const char* begin, const char* end);int spack(char type, int n, const char* data, int len);bool send_packet(const char* data, int size);
private:char data_[MaxSize];int maxl = MaxLen;int time = 10;int npad = 0;int padc = 64;int eol = END_CHAR;char qctl = '#';int last_size = 0;
};
虚函数列表:
- on_init 处理发送标识包(包括最大数据包size/time/npad/padc/eol/qctl)并发送出应答包。
- on_file_header 处理文件头包
- on_data 处理文件数据包
- on_end 处理文件结束包
- on_break 处理中断传输包
- on_ack 处理应答包
- on_nack 处理否定应答包
- on_error 处理错误包
- write 向串口写数据
- read 从串口读数据
- getc 从串口读取一个字符
函数列表:
- send_init 发送发送标识包
- send_data 发送文件数据包
- send_end 发送文件结束包
- send_break 发送中断传输包
- send_ack 发送应答包
- send_nack 发送否定应答包
- recv_packet 接收数据包并分发处理
- resend 重发数据
- encode 编码数据
- decode 解码数据
3.1.2 Kermit实现
- on_init/on_file_header/on_data/on_end/on_break/on_ack/on_nack/on_error
void Kermit::on_init(int /*seq*/, const char* data, int size)
{if(size > 0)maxl = unchar(data[0]);if(size > 1)time = unchar(data[1]);if(size > 2)npad = unchar(data[2]);if(size > 3)padc = unchar(data[3]);if(size > 4)eol = unchar(data[4]);if(size > 5)qctl = data[5];char d[6];d[0] = tochar(maxl);d[1] = tochar(time);d[2] = tochar(npad);d[3] = tochar(padc);d[4] = tochar(eol);d[5] = qctl;spack(Y, 0, d, sizeof (d));
}void Kermit::on_file_header(int seq, const char* data, int size) {std::cout << "on_file_header(" << seq << "," << std::string(data, size) << std::endl;
}void Kermit::on_data(int seq, const char* data, int size) {std::cout << "on_data(" << seq << "," << std::string(data, size) << std::endl;
}void Kermit::on_end(int seq, const char* data, int size) {std::cout << "on_end(" << seq << "," << std::string(data, size) << std::endl;
}void Kermit::on_break(int seq, const char* data, int size) {std::cout << "on_break(" << seq << "," << std::string(data, size) << std::endl;
}void Kermit::on_ack(int seq, const char* data, int size)
{if(seq != 0)return;if(size > 0)maxl = unchar(data[0]);if(size > 1)time = unchar(data[1]);if(size > 2)npad = unchar(data[2]);if(size > 3)padc = unchar(data[3]);if(size > 4)eol = unchar(data[4]);if(size > 5)qctl = data[5];
}void Kermit::on_nack(int seq, const char* data, int size) {std::cout << "on_nack(" << seq << "," << std::string(data, size) << std::endl;
}void Kermit::on_error(int seq, const char* data, int size) {std::cout << "on_error(" << seq << "," << std::string(data, size) << std::endl;
}
- send_init/send_data/send_end/send_break/send_ack/send_nack/resend
void Kermit::send_init()
{char data[6];data[0] = tochar(maxl);data[1] = tochar(time);data[2] = tochar(npad);data[3] = tochar(padc);data[4] = tochar(eol);data[5] = qctl;spack(S, 0, data, sizeof (data));
}void Kermit::send_data(int n, const char* data, int len) {spack(D, n, data, len);
}void Kermit::send_end(int n) {spack(Z, n, nullptr, 0);
}void Kermit::send_break(int n) {spack(B, n, nullptr, 0);
}
void Kermit::send_ack(int n) {spack(Y, n, nullptr, 0);
}void Kermit::send_nack(int n) {spack(N, n, nullptr, 0);
}void Kermit::resend() {send_packet(data_, last_size);
}
构造对应类型数据包并发送。
- recv_packet
bool Kermit::recv_packet()
{char ch = getc();if(ch != MARK)return false;ch = getc();int length = unchar(ch);if(length < MinLen)return false;//SEQ TYPE DATA CHECK <terminator>std::vector<char> data(length + 1, 0);if(read(data.data(), data.size()) != static_cast<int>(data.size()))return false;if(data.back() != eol)return false;uint16_t old_check = unchar(data.at(data.size() - 2));uint16_t new_check = check(ch, data.data(), data.data() + data.size() - 2);if(old_check != new_check)return false;char type = data[1];if(type == S)on_init(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == F)on_file_header(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == D)on_data(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == Z)on_end(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == Y)on_ack(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == N)on_nack(unchar(data[0]), data.data() + 2, data.size() - 4);else if(type == E)on_error(unchar(data[0]), data.data() + 2, data.size() - 4);return true;
}
接收数据包并校验,根据对应类型调用处理相应处理函数。
- encode/decode
int Kermit::encode(char a, char* data)
{int a7 = a & 127;int size = 0;if (a7 < 32 || a7 == 127){data[size++] = qctl;a = ctl(a);}else if (a7 == qctl){data[size++] = qctl;}data[size++] = a;data[size] = '\0';return size;
}int Kermit::decode(const char* data, char &b)
{const char *d = data;int a = *d++;if(a == qctl) {a = *d++;int a7 = a & 127;if(a7 < 62 && a7 < 96)a = ctl(a);}b = a;return d - data;
}
对数据进行编码/解码。
Qt实现Kermit协议(一) Qt实现Kermit协议(三)