弄清一个概念:
雷达通信是TCP通信,雷达是服务器端,而我们编的程序是客户端。
我们与决策端计算机进行通信是UDP,我们这边也是客户端,决策组那边是服务器端。
也就是说,我们编程的思路是,TCP客户端里面嵌入了UDP客户端。
一、编程的总体思路如下:
1、先雷达服务器端先connect,然后send,向雷达发送命令。
对于此,要涉及到一个概念:
雷达的数据的接收模式,我们可以向雷达发送三种命令,一种是单次测量:
单次测量:发送指令后 LMS 会返回当前 LMS 的测量数据
命令:sRN LMDscandata
返回的数据格式如下:
目前我用的就是这种解析模式。
连续测量:发送指令后,LMS 会实时返回其测量数据
命令:sEN LMDscandata 1
连续测量停止:发送指令后,LMS 停止向外发送测量数据
命令:sEN LMDscandata 0
每次都执行一遍TCP的客户端程序,有点麻烦,程序运行效率不太高。这点比较烦。
2、在第一步成功的条件下,我们就可以采集数据了。
这里有个很重要的一个地方需要大家注意,我要是没搞错的话,雷达服务器端,应该用的是一个可变的8位无符号整型来向我们发送数据的,也就是说,数据量 8*381 + 25*8 = 3248 byte,也就是说,我们接收端开辟的缓冲数据,必须大于这个数字,要记住这个值是一直在变动的。一定要开辟足够大的空间,不然数据量无法全部收满,不好整。
3、对接收到的数据,进行解析,根据数据格式一步步解析。
这部分没啥可讲的,重点就是一些数组、字符串、字符数组、动态开辟字符串等的灵活运用。
4、对解析出来的数据,进行处理,并通过UDP传至决策组。
这部分和之前的程序一样。不赘述。
二、编程的过程中,涉及到的一些基础点:
1、sendstr头尾各加上0x02和0x03
用字符数组实现:
重新定义dst[18] = {0,} //初始化的一种方式,这样剩下的其他的都自动初始化为0了。
char SendStr[16] = "sRN LMDscandata"; //将sendstr头尾各加上0x02和0x03 char dst[18]={0,}; int i = 0; dst[0] = 0x02; for( i=0; i < 15;i++) dst[i+1]=SendStr[i]; dst[16] = 0x03; dst[17] = ' ';
用格式化函数实现:
char SendStr[16] = "sRN LMDscandata"; char s[100]={0,}; sprintf(s,"%c%s%c",0x02,SendStr,0x03); printf("%s",s);
函数:sprintf的使用
函数功能:把格式化的数据写入某个字符串
函数原型:int sprintf( char *buffer, const char *format [, argument] … );
返回值:字符串长度(strlen)
对于这个格式化的数据,类似于C++里面CString类里面的format函数,都是用于格式化数据的。
现在我们用sprintf,用于格式化某个字符数组。
其字符数组与字符串之间可以相互转化
2、字符数组与字符串之间的转化
字符数组转字符串:
char SendStr[16] = "sRN LMDscandata"; string s; for (int i= 0;i<16; i++) { s+=SendStr[i]; }
字符串转字符数组:
char SendStr[16] = {0,}; string s = "sRN LMDscandata"; for (int i= 0;i<15; i++) //i只在这个for循环里面有效 { SendStr[i]=s[i]; } SendStr[16] = ' ';
3、清零函数的区别
指针变量存储在栈中,使用&运算后得到的是指针变量的地址.
数组名是个标签,没地方存放,没有地址,但在很多情况下取数组名地址的结果是仍然是其本身。可是我们应该不提倡对数组名取地址.
对于数组我们提倡用以下方式:
char szBuffer[4096] = {0,};
当然这种方式也是可以的,但不提倡
int szBuffer[4096];
//memset(szBuffer,0,sizeof(szBuffer));
ZeroMemory(szBuffer,sizeof(szBuffer));
对于结构体,我们提倡用下面的一种方式
Matrix matrix;
memset(&matrix,0,sizeof(matrix));
4、字符数组与字符指针变量的区别:
你这个问题问得很好,我们知道实际上一个字符串在进行赋值的时候,例如: char *p="hello world";这样赋值是允许的。那么这并不代表"hello world"这个就是一个指针,实际上我们的这一动作,编译器在幕后为我们做了很多事,首先在常量区分配一个字符数组,然后将"hello world"这个字符串中的字符(包括最后的' '字符。)拷贝到此数组(其实此数组我们可以看成是一个匿名数组,因为没有数组名)。然后返回数组首元素的地址,将此地址赋值给字符指针变量p。我们知道在c语言中没有字符串这种类型,在c语言中的字符串是通过字符数组来实现的,而且必须要求此字符数组具有' '结束符。这就是字符串与一般的字符数组不同的地方。
话说回来,你问到为什么char *p="hello world"; cout<<p<<endl;打印出来的是字符串,而不是地址。其实当初我也想到过类似的问题,没有找到权威的答案,但是我能够猜出几分。C++语言是兼容C语言的,所以在输出C串的时候是直接以给出的首地址,输出,直到遇到结束符' '结束。为什么会这样呢,我想着应该是语言的特性,或者是编译器的特性。既然C语言中字符串是通过带' '的字符数组实现的,那么请问如果编译器是由你来实现的,或者是printf()函数是你写的,要输出一个字符串的话你会怎么样呢,你是不是会输出指针的地址呢,cout<<p<<endl;这样的形式让读者一目了然,我要输出的就是一个字符串,而实际上p就是一个字符指针变量而已,为什么输出字符串,这在于cout的实现了,cout的实现我不的而知,但是可以肯定的是cout确实在遇到字符指针的时候会将做出这样的行为:将此指针当做首地址,向下输出字符,直到遇到' '结束为止。 这就是cout的行为,没有为什么。 其实你自己可以写一个自己的cout函数,输出地址而不是按照cout的默认行为。当然要实现cout的功能,必须要了解很多底层知识。
我们可以通过这样的方式输出字符指针的值,而不是输出以其为首地址的字符串。例如char a='A'; char *p=&a; cout<<(int)(p)<<endl; 只需要将指针的值(地址值实际上在内存中就是一个整数,标识了内存的地址,只不过编译器认为其实指针类型罢了,实际上从本质上来说,计算机中的任何类型在内存中都是以二进制的形式存在的,根本没有所谓的类型之分。为什么有类型,这就是编译器将底层抽象的结果)。
1. 字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘ ’作为串的结束。字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。
2. 对字符数组作初始化赋值,必须采用外部类型或静态类型,如: static char st[]={“C Language”};而对字符串指针变量则无此限制,如: char *ps="C Language";
3. 对字符串指针方式 char *ps="C Language";可以写为: char *ps;
ps="C Language"; 而对数组方式: static char st[]={"C Language"};不能写为: char st[20]; st={"C Language"};而只能对字符数组的各元素逐个赋值。
5、如何将字符串转为它所表示的数
字符数组对数组操作好操作,但个人认为可能麻烦了,这完全是C的思想,有些不可取
//此代码作用是从szbuffer中截取3个字符,这个其实是雷达的接收到的非零元素的个数, //s[3]=' ';是因为这是一个字符串而不是字符数组,字符数组后面是没有 的。 //最后,通过字符串转换函数将s字符串转为16进制无符号长整型,又由于tx是十进制的 //系统又强制转为10进制数,string to unsigned long ; char s[4]={0,}; unsigned long tx; for(int i=0; i<3; i++) s[i] = szBuffer[dCnt+i]; s[3] = ' '; tx = strtoul(s, 0, 16);//将字符串转换成无符号长整型数,16进制。
第二种方法:用sscanf函数。
参考下面的文章:
http://baike.baidu.com/view/1364018.htm
X 表示以十六进制形式输出
02 表示不足两位,前面补0输出;出过两位,不影响
举例:
printf("%02X", 0x123); //打印出:123
printf("%02X", 0x1); //打印出:01
%x表示已十六进制格式输出。
int a; char s[]="17D"; sscanf(s,"%x",&a);
sscanf函数原型:sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。
sscanf() - 从一个字符串中读进与指定格式相符的数据。
sprintf 与 sscanf 强化 char tem[100],a=0,b=0,c=0; sprintf(tem,"%c%c%c",'a',0x02,0x03);//与fprintf几乎一样,从后往前读。将后面的三个数 //以%c%c%c这种格式输入到tem当中去。下面类同。 sscanf(tem,"%c%c%c",&a,&b,&c);//与fscanf几乎一样,从前往后读 printf("%02x:%02x:%02x ", a, b, c);
6、可变数组传输与字符数组传输的比较
//对于用vector容器操作的函数,加上头文件<vector.h>,然后这玩意可以订阅可变数组,然后不用初始化,因为已经初始化过了,对于下面的memcpy可以省掉很多麻烦。 //还有就是可变数组的首地址是str[0],然后前面这个是强制转换为char* 型。后面也是一样。 uint len = (obs-1)*sizeof(Point) + 4; //vector<char> str(len); //声明可变数组 , 容器已经初始化过了。 //// memset(&str,0,sizeof(str)); //初始化,赋值为0 //memcpy((char*)&str[0],(char*)&matrix,str.size());//从结构体matrix里面截取部分长度,并赋值 给str数组。str.size() len char buffer[10000]; memcpy(buffer,(char*)&matrix,len);
UDPClient(buffer,len);
7、利用字符串函数实现copy功能,其中另一个字符数组的长度是不确定的
//动态开辟的数组与本身定义的数组不一样,这点要注意
//上面这句话可能不太好理解,我的意思是,动态开辟的字符数组与我们想自己定义的字符串(字符数组+’ ’)
//不太一样,假如单纯从值的角度考虑,我们所关心的是值而并不是最后一个 ,只要值即可,但从操作的角度
//考虑,我们需要吧这个动态数组当做字符串的话,那必须多开辟一个char空间来存放 ,之后的操作就和字符串
//操作一摸一样了。
char *copyDist =new char[count]; // strcpy(copy,OneDIST); //char copy[count]={0,};//没有这种写法,我们在栈空间里面开辟的数组必须是常数,长度可控的 memcpy(copyDist,OneDIST,count);
test:
int count = 4; char OneDIST[4]={'a','b','c',}; OneDIST[3] = ' '; char *copyDist =new char[count]; strcpy(copyDist,OneDIST);//用字符串复制函数也是可以的 //char copy[count]={0,}; //memcpy(copyDist,OneDIST,count);//这里用内存复制的形式将OneDIST字符串里面的东西全部给了copyDist //相当于copyDist也是一个字符串,然后,通过下面的字符串操作函数,解出copyDist所代表的值。 int x = strtoul(copyDist, 0, 16);
当然,除了strcpy函数之外,我们还有strcmp等
1.puts(字符数组) 输出
2.gets(字符数组) 输入
3.strcat(字符数组1,字符数组2) 字符串2接到字符串1的后面
4.strcpy和strncpy(字符数组1,字符串2,m) 将字符串2的前m个字符拷到str1中,最后加' '
5.strcmp(字符串1,字符串2) 两字符串比较
6.strlen(字符数组) 字符串的实际长度(不包括' ')
7.strlwr(字符串) 大写转换为小写
8.strupr(字符串) 小写转换为大写
strlen + 1 = sizeof