这是我参考mqtt服务器m
1,定义协议MQTT
类型 | 长度 | 数据 | checkSum |
Type | lenght | bin data | ~sum |
2,数据结构
struct MqttPacket { //定义Mqtt数据包
int type; //MQTT数据包首定节
int remlength; //Mqtt数据包内容长度
int8_t *remdata; //Mqtt数据内容
int _read_remlensize; //remlength占用字节数
int _read_remsize; //remdata占用字节
int _read_pos; //读取位置
};
3,收发逻辑
当遇到EAGAIN或EWOULDBLOCK等信号中断读取时返回为0,表示数据读取完成,下次来读的时候继续读
读取时(mosquitto就是这样读的):
//返回-1为断开,返回0表示读取成功(不一定读取完数据包哦)
int loop_handle_read(int fd, struct MqttPacket *inPacket) {
int rlen;
uint8_t byte;
if (inPacket->type == 0x00) { //读头:因为不会等于0x00
rlen = read(fd, &byte, 1);
if (rlen == 1)
inPacket->type = rlen; //下次读就不会读头
else if (rlen == 0)
return -1;
else if (errno == EWOULDBLOCK || errno == EAGAIN)
return 0; //读取不到,但不断开
else
return -1; //断开
}
if (inPacket->_read_remlensize <= 0) { //读可变长度
do {
rlen = read(fd, &byte, 1);
if (rlen == 1) {
inPacket->_read_remlensize--;
if (inPacket->_read_remlensize == -1)
inPacket->remlength = byte & 0x7F;
else
inPacket->remlength |= (byte & 0x7F) << 7;
if (inPacket->_read_remlensize <= -4)
break;
} else { //与上面相同不写}
} while (inPacket->_read_remsize <= 0);
inPacket->_read_remsize *= -1; //负负得正,下次就不再读可变长度了
inPacket->remdata = malloc(inPacket->remlength);
inPacket->_read_remsize = inPacket->remlength;
inPacket->_read_pos = 0;
}
while (inPacket->_read_remsize) {
rlen = read(fd, inPacket->remdata + inPacket->_read_pos,
inPacket->_read_remsize);
if (rlen > 0) {
inPacket->_read_remsize -= rlen; //当读取完后: _read_remsize一定为0
inPacket->_read_pos += rlen;
} else { //与上面相同不写}
}
//到此一个数据包就读取完成了
// 做数据包处理功能
//清空下面两个数据就会再读取
inPacket->type=0;
inPacket->_read_remlensize=0;
return 0;
}
4,处理分离
5,协议栈处理逻辑
通信协议栈(单工通信-不是C代码,回调方式-用C须要很多代码), 依据这种原理可以满足所有协议要求
基本协议:
Request:请求:[funid][data];
Response:响应[funid][YES/NO]; ACK:回应
封装:
方式一:
sendRequest(data) { send(fd,data...); } //发送请求数据
sendRequestWaitResponse(funid,data) //发送请求等待响应
{
sendRequest(data);
do{
recv(fd,rdata.);
if(rdata.funid==funid) return rdata.result[YES/NO]; //接收响应,是否成功
if(rdata.type==REQUEST) appendRequest(data); //添加到请求队列
while(time<10); //自己设定时间
}
readData() { //接收数据
if(data.type==REQUEST)
appendRequest(data);
.....
}
appendRequest(data) { //追加请求数据,如果在发送请求时收到对方的请求
queueFrame.app(data);
}
requestHandle() //处理对方的请求数据
深入实现逻辑(使用回调函数, 单线程逻辑处理):
数据帧: FuniD=0x02时的协议格式:[id][commandclass] [cmd] [data]
发送方用请求发送后,接收方以请求回应,id表示哪个设备
当commandclass==0x80代表是一个加密层,其数据内容才是须要的.
Queue queue_app, queue_raw; //两个队列,表示应用层数据,原始层数据
/**
*scheme表示在哪一层: 0:原始层,1:加密层; callback为回调,发送结果
*参数放入应用层队列,发送完后回调
*/
app_send(id,data[cls,cmd,...],scheme,callback(int) ||arg ) { queue_app.append(arg); }
/**
*加密层发送,发送完后回调
*/
_send_crypt(id,data[...], c_callback(int):arg ) { //注意这里有回调函数(在下面的回调函数中调用)
cdata[0x80,AES,`password`]; //发送加密说明
_send_raw(id,cdata, toraw_callback(int){ //有点像object-c
_send_raw(id, encode(data), c_callback:arg ); ///原始层发送完成之后的回调到这来,发送加密数据
});
}
/**
*原始层发送,参数上下文放入原始层队列,发送完后由原始层回调
*/
_send_raw(id,data[...],callback(int)) {
queue_raw.append(args[id,data,callback]); //添加到原始层发送队列
}
/// 处理应用层数据(核心实现)
handle_app() {
arg=queue_app.pop(); //从队列中得到数据
if(arg.scheme==0) _send_raw(args[id,args.data,callback]); //原始发送
if(args.scheme==1) _send_crypt(args[id,args.data,callback]); //加密发送
}
/// 处理原始层发送(核心实现)
handle_raw() {
arg=queue_raw.pop();
ret=sendRequest(arg[id,cls,cmd...]); //这里,发送并等待结果
arg.callback(ret:true,arg); //回调发送是否成功的结果
}
则,在单一线程里面只需要
while(1) { handle_app(); handle_raw(); }
然后,你只需像queu_raw或queue_app队列扔数据就可以了
//如果: cmd=Get 须要对方cmd=Report, 对方回来数据后到 callback回调函数位置
app_send_get(id, data[cls,cmd:Get,..], wait[cls,cmd:Report], callback(status, data:report)){
app_send(id,data,scheme=auto, _get_backfun(){//发送到应用层
//应用层发送成功后,回调这里,存在请求队列
list_request.append(wait, _report_backfun(status,data:report){
callback(status, data:report); //回去了
});
});
}
handleRequestFrame(){ //处理数据帧
frame=queueFrame.pop(); //看appendRequestFrame
list_request.pop(frame.id,frame.cls, frame.cmd, _sucess(wait, _report_backfun) {
_report_backfun(YES, frame.todata); //这样就回去了
}
}
}