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

项目背景:在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;

                 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值