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 95
SEQ One character, packet sequence number + 32, modulo 64
TYPE One character, an uppercase letter
CHECK 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 Data
Z = 0x5A, //End of File.
B = 0x42, //Break Transmission, end of transaction.
Y = 0x59, //Acknowledgment
N = 0x4E, //Negative Acknowledgment
E = 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协议(三)