Android java层与C层通过localsocket通信、通信协议制定与解析。

来源:互联网 发布:三维动画制作软件工具 编辑:程序博客网 时间:2024/05/22 01:36

项目背景:在Android系统中,Java层通过localsocket与Framework C层进行通信。

1、通信协议

 

帧头

源地址

目的地址

帧类型

内容长度

正文内容

帧尾

1字节

0xaf

1字节

 

1字节

1字节

2字节

不定长

发往高清板的内容依据“HD3_LS_LB001_V1_02_2”组织

发往前面板的内容依据操作码协议组织

发往rs232的内容依据RS232协议组织

1字节

0x5f

 

帧头:一个字节,固定位0xaf.地址(共2字节):源地址1字节;目的地址1字节

地址定义

FRONTPANEL_ADDR                                    1(0X1)

HIFIPANEL_ADDR                                         2(0X2)

ANDROIDPANEL_ADDRESS                        3(0X3)

APP_ADDRESS                                               4(0X4)

COMPUTER_ADDR                                       10(0XA)

IR_ADDR                                                         010000(0X)

红外模块地址用01000表示(避开1111的取值范围)

 

帧类型:1字节

类型分类:(M3-àM4时有用。)M4-àM3时,类型为默认为0M3端不解析

 

0=应答帧

1=不需要应答

2=接收到最后数据帧应答(默认)

3=接收后立即应答

内容长度:2字节

 内容:不定长

帧尾:1字节 固定0x5f


==================================================

在我的项目中,Java端处理完逻辑后需要快速地向C层发送多个长短不一的数据(在代码中就是顺序多次调用发送函数)。在测试中发现,在接收端会出现粘包现象。

注意:使用socket通信时,如果采用长连接、TCP、快速发送小数据包,就很有很能产生粘包现象。

为解决粘包可以采取以下几种解析方法。

方法一:在接收端先找到0xaf字节表示一帧开始,接着再找下一个0x5f字节,如果找到,就把该区间的内容作为一帧进行进一步解析(地址、帧类型、内容长度、内容)。然后以0x5f为基准,接着找下一帧。

for(i=0;i<len_buffer_socket_parse;i++){if(buffer_socket_parse[i]==Packat_Header){index_begin=i;index_temp=index_begin;for(;index_temp<len_buffer_socket_parse;index_temp++){if(buffer_socket_parse[index_temp]==Packat_End){index_end=index_temp;//发送到socket处理for(cursor=index_begin,j=0;cursor<=index_end;cursor++,j++){socket_frame_content[j]=buffer_socket_parse[cursor];SLOGE("cursor=%d,socket_frame_content[%d]=%x",cursor,j,socket_frame_content[j]);}SLOGE("socket_frame_content[0]=%x,socket_frame_content[%d]=%d",socket_frame_content[0],j-1,socket_frame_content[j-1]);addr = socket_frame_content[1]>>2;contentlen = socket_frame_content[2]; //正文长度for(index=0;index<contentlen;index++){content[index]=socket_frame_content[index+3];SLOGE("addr=%d,contentlen=%d,content[%d]=%x",addr,contentlen,index,content[index]);}switch(addr){case 20: //音量控制//TODO 转发至高清板write(fd_uart_debug,buffer_socket_parse,len_buffer_socket_parse);TWE_COMM_TX(content,contentlen,0x32,CommPackType_LastAck);send(s_fdCommand,buffer_socket_parse,len_buffer_socket_parse,0);break;case FRONTPANEL_ADDR://发送到前面板SLOGE("COMMAND TO frontpanel");TWE_COMM_TX(content,contentlen,0x31,CommPackType_LastAck);write(fd_uart_debug,content,contentlen);break;case HIFIPANEL_ADDR://发送到高清板SLOGE("COMMAND TO hifipanel");TWE_COMM_TX(content,contentlen,0x32,CommPackType_LastAck);break;case COMPUTER_ADDR://发送到电脑SLOGE("COMMAND TO computer");break;case IR_ADDR://发送到红外模块SLOGE("COMMAND TO ir module");sendlen=send(fd_domain,&buffer_socket_parse[3],sizeof(uchar),0);if(sendlen<0){SLOGE("send command to ir module error");} else {SLOGE("send command to ir module sendlen=%d",sendlen);}break;case 6:break;case 7:break;case 8:break;case 9:break;case 11:break;case 12:break;case 13:break;default:SLOGE("ERROR:TWE_Socket_Loop switch");break;}i=index_end;break;}}}}

该种解析方法的优点是思路简单,缺点是如果内容包含字节0xaf或0x5f时,该方法不能准确地从粘包中提取出内容。

方法二:从接收缓冲区接收到数据,无论粘包或是不粘包,第一个字节必然是0xaf。基于这个前提,我们可以从0xaf开始,使用状态机,依据协议一个字节一个字节的开始解析缓冲区的数据,依据内容长度找出内容。

该方法的缺点是在基于跨网络的socket通信中可能会出现问题,优点是可以准确地识别出内容,即使内容中包含0xaf或0x5f。代码如下:

if(len_buffer_socket_parse>0){SLOGE("buffer_socket_parse[0]=%x,s_fdCommand=%d",buffer_socket_parse[0],s_fdCommand);for(i=0;i<len_buffer_socket_parse;i++){dataIn = buffer_socket_parse[i];SLOGE("status_socket=%d,dataIn=%x",status_socket,dataIn);switch(status_socket){case 0:if(dataIn==Packat_Header){status_socket++;index=0; //要记得清零,我在这里犯过一次错误}else{i=len_buffer_socket_parse; //该数据报有问题、丢弃SLOGE("第一个字节有误");}break;case 1: //源地址if(dataIn==HIFIPANEL_ADDR||dataIn==FRONTPANEL_ADDR||dataIn==IR_ADDR||dataIn==ANDROIDPANEL_ADDRESS){source_addr=dataIn;status_socket++;}else{status_socket=0;i=len_buffer_socket_parse;SLOGE("源地址有误");}break;case 2: //目的地址if(dataIn==HIFIPANEL_ADDR||dataIn==FRONTPANEL_ADDR||dataIn==IR_ADDR){dest_addr=dataIn;status_socket++;}else{    SLOGE("目的地址有误");status_socket=0;i=len_buffer_socket_parse;}break;case 3://帧类型if(dataIn==CommPackType_ACK||dataIn==CommPackType_NoAck||dataIn==CommPackType_EachFrameACK||dataIn==CommPackType_LastAck){frame_type=dataIn;status_socket++;}else{    SLOGE("帧类型有误");status_socket=0;i=len_buffer_socket_parse;}break;case 4: //内容长度contentlen=dataIn;contentlen_temp=contentlen;status_socket++;break;case 5: //内容获取content[index]=dataIn;index++;if(--contentlen_temp==0) {status_socket++;for(j=0;j<contentlen;j++){SLOGE(" to send content[%d]=%x",j,content[j]);}}break;case 6://帧尾if(dataIn==Packet_End){status_socket=0;SLOGE("dest_addr=%d",dest_addr);switch(dest_addr) {    case 20://音量控制//TODO 转发至高清板write(fd_uart_debug,buffer_socket_parse,len_buffer_socket_parse);TWE_COMM_TX(content,contentlen,0x32,CommPackType_LastAck);send(s_fdCommand,buffer_socket_parse,len_buffer_socket_parse,0);break;case FRONTPANEL_ADDR://发送到前面板SLOGE("COMMAND TO frontpanel");TWE_COMM_TX(content,contentlen,0x31,CommPackType_LastAck);write(fd_uart_debug,content,contentlen);break;case HIFIPANEL_ADDR://发送到高清板SLOGE("COMMAND TO hifipanel");TWE_COMM_TX(content,contentlen,0x32,CommPackType_LastAck);break;case COMPUTER_ADDR: //发送到电脑SLOGE("COMMAND TO computer");break;case IR_ADDR://发送到红外模块SLOGE("COMMAND TO ir module");sendlen=send(fd_domain,&content[0],sizeof(uchar),0);if(sendlen<0){SLOGE("send command to ir module error");}else{SLOGE("send command to ir module sendlen=%d",sendlen);}break;default:SLOGE("ERROR:TWE_Socket_Loop send error");break;}}else{status_socket=0; //丢弃i=len_buffer_socket_parse;}break;default:break;}}}len_buffer_socket_parse = 0;

                 

0 0