移*动互*联网数*据采*集系*统
//======================================================
/***********移动互联网数据采集系统************** >功能: > 1:项目分为服务器、客户端;服务器交互对象为客户端及WATCHER开发板 > 2:客户端功能:注册、登陆、退出、删除、查询、控制及设置功能 > 3:服务端功能:客户管理、更新数据库、数据管理排序等 >设计关键词: > 内核链表(数据保存/排序/打印)、数据库(所有数据保存位置)、自定义协议(服务器与WATCHER板交互)、 > Json协议(服务器与客户端交互)、非堵塞(超时检测)、TCP协议、线程(新建线程实现)、心跳检查(2分钟) >WATCHER开发板智能控制协议主要指令: > ①RGB灯光控制指令/RGB灯光控制反馈指令,②温湿度查询指令/温湿度查询反馈指令 > ③光照强度查询指令/光照强度查询反馈指令,④时钟设置指令/时钟设置反馈指令 > ⑤闹钟设置指令/ 闹钟设置反馈指令,⑥闹钟查询指令/闹钟查询反馈指令 > ***********移动互联网数据采集系统(ser)**************/ #include"data_collect.h" //=====================消息类型===================================== proto ptype[] = { {QUERY_INFORMATION, query_information} , /*查询*/ #if 0 {CONTROL_LAMPLIGHT, control_lamplight} , /*控制*/ {SET_ATTRIBUT, set_attribut} , /*设置*/ #endif {CLIENT_LOGIN, server_check_login} , /*登陆验证*/ {CLIENT_REGISTER, register_new_client} , /*注册*/ {CLIENT_EXIT, client_exit} , /*客户退出*/ #if 0 {CLIENT_DELETE, client_delete} , /*客户删除*/ {TGB_LAMP_SET, tgb_lamp_set} , /*tgb灯*/ #endif {TEMP_HUMIDITY_QUERY, temp_humidity_query} , /*温湿度*/ {LIGHT_INTENSITY_QUERY, light_intensity_query} , /*光照强度*/ #if 0 {TIME_SET, time_set} , /*时钟设置*/ {CLOCK_SET, clock_set} , /*闹钟设置*/ {CLOCK_QUERY, clock_query} , /*闹钟查询*/ {CONTROL_FEEDBACK, control_feedback} , /*控制反馈*/ {QUERY_FEEDBACK, query_feedback} , /*查询反馈*/ #endif {0, 0} }; //=================主函数(main)入口=========================== int main(int argc, char *argv[]) { int listen_fd; int ret; state_seq = 0; pthread_t tid = 0; devboard_thr_online = 0; //需要用自动查询时候改为0 int line_num = 0; //新建一个链表其值加1 /*=======信号处理函数、语句(时钟、退出)====== ===============================================*/ //=====1,create a tcp socket=========== listen_fd = socket(AF_INET,SOCK_STREAM,0); //socket创建特殊文件描述符,IPV4,字节流(TCP协议) if(listen_fd <0){ perror("socket"); exit(1); } //=====2,setsockopt-- set internet attribute==== int b_reuse = 1; if(setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)) == -1) //设置属性,允许重用本地地址和端口 printf("server setsockopt() error"); //原为err //=====3, bind port and addr============ struct sockaddr_in srv_addr; //定义绑定时的结构体,结构体包含自己的族类型、IP、端口及一定填充区域 bzero (&srv_addr, sizeof (srv_addr)); //对结构体清零 srv_addr.sin_family = AF_INET; //地址族IPV4 srv_addr.sin_port = htons(SERV_PORT); //端口号转换host to network short,默认值9999 srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //指定对方IP号为任意IP均可 ret = bind(listen_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr)); //bing绑定(在自己打开的文件描述符listen_fd上,绑定自己的端口号及指定对方可连接的ip号) if( ret <0 ){ perror("bind error"); exit(1); } //=====4, listen-- set max connection number====== if(listen(listen_fd,QUEUELEN) == -1){ //设置同一时间最多可连接5个客户端 perror("listen error\n"); exit(1); } fprintf(stderr,"server waiting client connection ......OK\n"); //=====5、初始化内核链,设置链表、select、accept参数变量===== //=====a)定义内核链表变量、初始化链表===== struct cli_info mylist; // 定义一个结构体变量(包含内核链表成员) INIT_LIST_HEAD(&mylist.list); // 初始化链表头 struct cli_info *tmp = NULL; //定义结构体指针(包含内核链表成员) struct list_head *pos,*q; //定义遍历内核链表用的临时指针 //=====b)定义select及accept参数变量===== fd_set rset; //定义数组集合,存放文件描述符,结构体数组成员在select()前后会变化 int maxfd = -1; //定义select()的参数(最大文件描述符号,计算rset数组最大位长度) struct timeval tout; //select超时堵塞设置 int new_fd = -1; //定义accept连接时创建的新文件描述符变量 //pthread_t tid; //定义一个线程????????????????? struct sockaddr_in cli_addr; //定义文件描述符属性的结构体,结构体包含自己的族类型、IP、端口及一定填充区域 /*=====6、循环,内核链表对所有连接客户端的结构体进行连接, select监控有数据的客户,对已连接有数据客户端建立线程处理需求==*/ while(1){ //===a)select参数的变量赋初值==== tout.tv_sec = 0; //设置select等待超时为2s tout.tv_usec = 0; FD_ZERO(&rset); //清空rset数组队列 FD_SET(listen_fd,&rset); //把fd加入到rset数组中,seleten时监控是否有新连接 maxfd = listen_fd; //改变rset数组最大位长度 //===b)通过内核链表,遍历用户属性结构体,把建立好连接的fd加入到rset,同时把maxfd更新为值最大的fd==== list_for_each_safe(pos,q,&mylist.list){ tmp = list_entry(pos,struct cli_info,list); //tmp指向遍历的某一项结构体数据 FD_SET(tmp->conn_fd,&rset); //将包含内核链表的结构体内成员(文件描述符)加入到rset数组中 if(maxfd < tmp->conn_fd){ maxfd = tmp->conn_fd; //改变rset数组最大位长度 } if(devboard_thr_online == 1){ //如果开发板线程不在线(即有线程正在向开发板发送数据),则不再另外新建线程 if (tmp->conn_addr.sin_addr.s_addr == inet_addr(BOARD_IP)) { //如果开发板有连接,则创建线程,等等两秒查询一次,并写入数据库 if ((pthread_create (&tid, NULL, board_pthreads, (void *)tmp)) == -1) printf ("server pthread_create() 2 error"); } } } //===c)调用select多路复用监控函数,调用后有数据的连接将保留在select数组内===== ret = select(maxfd+1, &rset, NULL, NULL, &tout); //多路复用监控函数,设置读端 //===d)accept,是否有新的连接请求过来(给新连接申请空间并加入内核链表)====== socklen_t len = sizeof(struct sockaddr_in); if (FD_ISSET (listen_fd, &rset)) { new_fd = accept (listen_fd, (struct sockaddr *) &cli_addr, &len); /* accept()处理新的连接 */ if (new_fd < 0) { perror ("accept"); } else{ //若连接成功,则把对新连接申请空间,并将连接加入内核链表 printf ("new client coming..cli ip = %s, port(%d)\n", inet_ntoa (cli_addr.sin_addr), ntohs (cli_addr.sin_port)); tmp = (struct cli_info *)malloc(sizeof(struct cli_info)); /*把conn_fd加入到cli_info的链表里面 */ if(!tmp) { perror("malloc"); } else{ //填充tmp指向的客户属性结构体 list_add (&(tmp->list), &(mylist.list)); //把tmp->list加入用户信息的链表中 tmp->head = &mylist.list; //建立线程后,链表成员退出(即删除)时使用 tmp->conn_fd = new_fd; //将连接的新文件描述符conn_fd,赋值给新申请的结构体成员tmp->conn_fd tmp->conn_addr = (struct sockaddr_in )cli_addr; line_num++; tmp->num = line_num; printf ("1,tmp->conn_fd =%d,tmp->num =%d\n",tmp->conn_fd,tmp->num); } } } /*====e)已经建立好连接的客户端是否有送过来数据 =============*/ /*遍历内核链表,依次判断链表项中的conn_fd上是否有数据,如有,则读出来处理*/ list_for_each_safe (pos, q, &mylist.list) { tmp = list_entry (pos, struct cli_info, list); //tmp指向遍历到的某一项结构体数据 if(FD_ISSET(tmp->conn_fd, &rset)) { /* 已建立连接的客户端fd上有数据*/ if ((pthread_create (&tid, NULL, pthreads, (void *)tmp)) == -1) //为能传链表头 printf ("server pthread_create() 2 error"); //原为err printf("已创建一个新线程,其连接通信时的new_fd = %d\n",tmp->conn_fd); usleep(200000); //不睡眠,如果有数据就会创建线程进20个,原因尚待进一步查找?? } } } close(listen_fd); return 0; } struct stu_info st; //?????????????????????????????????????????? void *board_pthreads(void *arg) { pthread_detach (pthread_self ()); //设置线程属性分离 struct cli_info *client_attr = ((struct cli_info *)arg); //将arg(即主函数有数据的连接属性结构体tmp)的地址赋值给tmp int send_num,ret; unsigned char recv_buf[CMD_MAX_LEN]; struct stu_info st; devboard_thr_online = 0; auto_query = (auto_query)%255+1; //发送序列号,可设置若相同则不接受 printf("AUTO_QUERT_TIME/U_SECOND=%d\n",(AUTO_QUERT_TIME*1000000)/U_SECOND); for(send_num=0;send_num<(AUTO_QUERT_TIME*1000000)/U_SECOND;send_num++) //若发送SEND_NUM次后对方仍然未接收到数据则跳出(接收失败) { printf("进入发送循环\n"); send_dev_board(client_attr); usleep(U_SECOND); //发送后、等待U_SECOND us再接收数据 bzero (recv_buf, CMD_MAX_LEN); //将输入变量清零 do { ret = recv (client_attr->conn_fd, recv_buf, CMD_MAX_LEN - 1, MSG_DONTWAIT); } while (ret < 0 && EINTR == errno); if (ret < 0){ printf("send num = %d\n",send_num); }else{ string_to_stu(&st,recv_buf); //拆包到st结构体 recv_dev_board(&st); break; } } if(send_num<(AUTO_QUERT_TIME*1000000)/U_SECOND){ printf("package information:st.flag=0x%x,st.len=0x%x,st.cmd=0x%x,st.seq=0x%x,\nst.pac_data[0]=0x%x,st.pac_data=%s,st.bcc=0x%x\n", st.flag,st.len,st.cmd,st.seq,st.pac_data[0],st.pac_data,st.bcc); printf("当前时间%s:,当前温度为%d,当前湿度为%d\n",dev_board_data.data_time,st.pac_data[2],st.pac_data[4]); }else printf("\n\n已连接,但未接收到开发板上信息\n\n\n"); if(AUTO_QUERT_TIME > U_SECOND*send_num) sleep(AUTO_QUERT_TIME - U_SECOND*send_num); devboard_thr_online = 1; pthread_exit(0); } void *pthreads (void *arg) { pthread_detach (pthread_self ()); //设置线程属性分离 struct cli_info *client_attr = ((struct cli_info *)arg); //将arg(即主函数有数据的连接属性结构体tmp)的地址赋值给tmp unsigned char buf[BUFSIZ]; //定义缓冲区数组变量 int ret = -1; struct stu_info st; //拆包后结构体 // printf ("**********************tmp->num =%d\n",client_attr->num); //如果不注释,此处不断创线程打印,不知何故?? assert (client_attr); if (!client_attr) pthread_exit(0); do { ret = recv (client_attr->conn_fd, buf, BUFSIZ-1, 0); //recv()接收数据 } while (ret < 0 && EINTR == errno); if (ret < 0){ perror("recv"); pthread_exit(0); } // printf ("555*****tmp->num =%d\n",client_attr->num); //如果不注释,此处不断创?????????????? char addr[50]={0}; strcpy(addr,inet_ntoa(client_attr->conn_addr.sin_addr)); // printf("send addr = %s \n\n",addr); ???????????????????????????????????????????????????????? //======否则为客户发送的温湿度查询指令,需给开发板发送指令收到命令后,反馈该客户温湿度值=============== struct list_head *pos,*q; //定义遍历内核链表用的临时指针 struct cli_info *tmp = NULL; //定义结构体指针(包含内核链表成员) #if 1 if(client_attr->num == 1) { printf ("*******************接收到开发板反馈的数据*******************\n"); string_to_stu_tmep(&st,buf); recv_dev_board(&st); recv_content(buf); //打印接收内容 printf("当前时间%s:,当前温度为%d,当前湿度为%d\n\n",dev_board_data.data_time,st.pac_data[2],st.pac_data[4]); pthread_exit(0); } // } #else pos = (client_attr->head)->next; pos = pos->next; tmp = list_entry (pos, struct cli_info,list); //client_attr指向遍历到的某一项结构体数据 if(tmp->list.next == client_attr->list.next){ printf ("\n1 接收到开发板反馈的数据\n\n"); string_to_stu_tmep(&st,buf); recv_dev_board(&st); recv_content(buf); //打印接收内容 printf("package information:st.flag=0x%x,st.len=0x%x,st.cmd=0x%x,st.seq=0x%x,\nst.pac_data[0]=0x%x,st.pac_data=%s,st.bcc=0x%x\n", st.flag,st.len,st.cmd,st.seq,st.pac_data[0],st.pac_data,st.bcc); printf("当前时间%s:,当前温度为%d,当前湿度为%d\n",dev_board_data.data_time,st.pac_data[2],st.pac_data[4]); pthread_exit(0); } #endif printf("*******************不是开发板发过来的数据*******************\n"); string_to_stu(&st,buf); //拆包到st结构体 #if 0 printf("buf=%s\n",buf); printf("buf[2]=%d\n",(int)buf[2]); printf("buf[4]=%d\n",buf[4]); printf("buf+5=%d\n",(int)buf[5]); printf("buf[6]=%d\n",buf[6]); //#else printf("sizeof(start_h): %d\n", sizeof(buf[0])); printf("sizeof(cmd): %d\n", sizeof(buf[4])); printf("start_h 0: 0x%x\n", buf[0]); /* 包头(共2字节),此处为高位字节 */ printf("start_l 1: 0x%x\n", buf[1]); /* 包头(共2字节),此处为高位字节 */ printf("len_h 2: 0x%x\n", buf[2]); /* 包长度(共2字节),此处为高位字节 */ printf("len_l 3: 0x%x\n", buf[3]); /* 包长度(共2字节),此处为低位字节 */ printf("cmd 4: 0x%x\n", buf[4]); /* 命令类型(共1字节),因为查询温湿度,所以查询指令填0x80 */ printf("seq 5: %x\n", buf[5]); /* seq为消息序,两个作用:消息重发和识别客户端 */ printf("data[0] 6: %x\n", buf[6]); /* 设备类型,要查询温湿度传感器,所以为0x02 */ printf("data[1] 7: %x\n", buf[7]); /* 反馈结果(成功:0x00,BCC校验错误:0x01,失败:0x02,模式错误:0x03,指令非法:0xFF*/ printf("data[2] 8: %x\n", buf[8]); /* 湿度整数部分 */ printf("data[3] 9: %x\n", buf[9]); /* 湿度小数部分 */ printf("data[4] 10: %x\n", buf[10]); /* 温度整数部分 */ printf("data[5] 11: %x\n", buf[11]); /* 温度小数部分 */ printf("bcc 7 12: %x\n", buf[12]); /* 校验值 */ printf("package information:st.flag=0x%x,st.len=0x%x,st.cmd=0x%x,st.seq=0x%x,\nst.pac_data[0]=0x%x,st.bcc=0x%x\n", st.flag,st.len,st.cmd,st.seq,st.pac_data[0],st.bcc); #endif int i = 0; for (i = 0; ptype[i].fun_flag != 0; i++) { if (st.pac_data[0] == ptype[i].fun_flag) { ptype[i].fun (&st,client_attr); //根据发送类型,处理相关内容函数 break; } } printf("*******************关闭遍历的线程*******************\n"); //???????? pthread_exit(0); } //#########装拆包处有问题8.10 //sleep(2); //?????????????????????????????????????????????????????????????????? //printf("1_ 哪里出错了??\n"); //????????????????????????????????????????????????????????????????????????
//======================================================
/***********移动互联网数据采集系统************** >project name: move internet data collect system >Author: 夏敏、甘香鹏、程健、殷文杰 >Created Time :2016/08/04 18:20 >功能: > 1:项目分为服务器、客户端;服务器交互对象为客户端及WATCHER开发板 > 2:客户端功能:注册、登陆、退出、删除、查询、控制及设置功能 > 3:服务端功能:客户管理、更新数据库、数据管理排序等 >设计关键词: > 内核链表(数据保存/排序/打印)、数据库(所有数据保存位置)、自定义协议(服务器与WATCHER板交互)、 > Json协议(服务器与客户端交互)、非堵塞(超时检测)、TCP协议、线程(新建线程实现)、心跳检查(2分钟) >WATCHER开发板智能控制协议主要指令: > ①RGB灯光控制指令/RGB灯光控制反馈指令,②温湿度查询指令/温湿度查询反馈指令 > ③光照强度查询指令/光照强度查询反馈指令,④时钟设置指令/时钟设置反馈指令 > ⑤闹钟设置指令/ 闹钟设置反馈指令,⑥闹钟查询指令/闹钟查询反馈指令 > ***********移动互联网数据采集系统(ser)**************/ #include"data_collect.h" /******************************************* //==============温湿度查询,将字符串转换成数据包================== void stu_to_sti(unsigned seq,unsigned char *buf) { buf[0] =0xff; //flag头部首字节,开始标志位固定为0xff buf[1] =0xff; //flag头部次字节 buf[2] =0x00; //length首字节,length为协议长度:从cmd开始到正规协议结束所占用字节数; buf[3] =0x04; //length次字节 buf[4] =0x80; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =(unsigned char)seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 buf[6] =0x02; //数据,其中data[0]为数据类型 } // 填充协议: 命令会有9个自己的开销(包含最后的'\0'字符) buf[0] = PACK_HEAD; //head buf[1] = cmd_type; // cmd_type buf[2] = 0x0; //2字节长度,高位在前,低位在后 buf[3] = cmd_len & 0xff; if (cmd_len > 255) { buf[2] = ((cmd_len & 0xff00) >> 8); // buf[3] = cmd_len & 0xff; ***********************************************/ //============(拆包,通用)将开发板上的包变成结构体====================== void string_to_stu(struct stu_info *st,unsigned char *buf) { memset(st,0,sizeof(st)); int i; printf("*0_******通用协议收包buf[0]=0x%x\n*********",buf[0]); st->flag = buf[1]; //flag头部,开始标志位固定为0xffff if(buf[0]>0) //判断flag头部高八位是否有值 st->flag += buf[0]*256; printf("*1_******通用协议收包buf[0]=0x%x\n*********",buf[0]); st->len = buf[3]; //length长度,length为协议长度:从cmd开始到正规协议结束所占用字节数; if(buf[2]>0) //判断length长度高八位是否有值 st->len += buf[2]*256; st->cmd = buf[4]; //cmd指令类型(控制/控制反馈/查询/查询反馈) st->seq = buf[5]; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 // memcpy (st->pac_data, buf+6, st->len); //客户/开发板数据,其中data[0]为数据类型 for(i=0;i < st->len; i++){ st->pac_data[6+i] = buf[2]; } st->pac_data[st->len] = '#'; st->bcc = buf[st->len+6]; //校验和:数据校验,从length到data数据的异或校验和 #if 1 printf("*******通用协议收包buf[0]=0x%x\n*********",buf[0]); printf("buf[0]=0x%x\n",buf[0]); printf("buf[1]=0x%x\n",buf[1]); printf("buf[2]=0x%x\n",buf[2]); printf("buf[3]=0x%x\n",buf[3]); printf("buf[4]=0x%x\n",buf[4]); printf("buf[5]=0x%x\n",buf[5]); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); printf("buf[len+6]=0x%x\n",(int)buf[st->len+6]); printf("len=%d\n",st->len); //????????????????????????????????????? printf("st->pac_data[0]=%c\n",st->pac_data[0]); //????????????????????????????????????? // printf("拆解后的字符串=%s\n",st->pac_data); //???????????????????? printf("st->bcc=%x\n",(int)st->bcc); #endif } //==============(封包,通用)将字符串转换成数据包========== void stu_to_sti(unsigned char cmd,unsigned char seq,unsigned char *data,unsigned char *buf) { int len =0; for(len=0;data[len] != '#'; len++); bzero(buf,len+20); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); buf[0] =0xff; //flag头部首字节,开始标志位固定为0xffff buf[1] =0xff; //flag头部次字节 if(len > 255){ buf[2] = len/256; buf[3] = len - buf[3]*256; }else{ buf[2] = 0; buf[3] = len; } buf[4] =cmd; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 // memcpy (buf+6,data,len); //客户/开发板数据,其中data[0]为数据类型 int i; for(i=0 ;data[i] == '#'; i++){ buf[6+i] = data[i]; } buf[len+6] =buf[2]^buf[3]^buf[4]^buf[5]^buf[6]; //校验和:数据校验,从length到data数据的异或校验和 // buf[len+7] = '\0'; #if 1 //测试用 printf("buf[0]=0x%x\n",buf[0]); printf("buf[1]=0x%x\n",buf[1]); printf("buf[2]=0x%x\n",buf[2]); printf("buf[3]=0x%x\n",buf[3]); printf("buf[4]=0x%x\n",buf[4]); printf("buf[5]=0x%x\n",buf[5]); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); printf("buf[len+6]=0x%x\n",(int)buf[len+6]); #endif } //==============(封包,开发板)温湿度查询,将字符串转换成数据包========== void stu_to_sti_tmep(unsigned char seq,unsigned char *buf) { bzero(buf,7); buf[0] =0xff; //flag头部首字节,开始标志位固定为0xffff buf[1] =0xff; //flag头部次字节 buf[2] =0x00; //length首字节,length为协议长度:从cmd开始到正规协议结束所占用字节数; buf[3] =0x04; //length次字节 buf[4] =0x80; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =(unsigned char)seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 buf[6] =0x02; //数据,其中data[0]为数据类型 buf[7] =buf[2]^buf[3]^buf[4]^buf[5]^buf[6]; //校验和:数据校验,从length到data数据的异或校验和 } #if 1 //==============(拆包,开发板)温湿度查询,将字符串转换成数据包========== void string_to_stu_tmep(struct stu_info *st,unsigned char *buf) { memset(st,0,sizeof(st)); st->flag = buf[0]; //flag头部,开始标志位固定为0xffff if(buf[1]>0) //判断flag头部高八位是否有值 st->flag += buf[1]*256; st->len = buf[2]; //length长度,length为协议长度:从cmd开始到正规协议结束所占用字节数; if(buf[3]>0) //判断length长度高八位是否有值 st->len += buf[3]*256; st->cmd = buf[4]; //cmd指令类型(控制/控制反馈/查询/查询反馈) st->seq = buf[5]; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 st->pac_data[0] = buf[6]; st->pac_data[1] = buf[7]; st->pac_data[2] = buf[8]; st->pac_data[3] = buf[9]; st->pac_data[4] = buf[10]; st->pac_data[5] = buf[11]; st->bcc = buf[12]; //校验和:数据校验,从length到data数据的异或校验和 st->pac_data[6] = '#'; } #endif //温湿度 void temp_humidity_query(struct stu_info * p, cli_info * client_attr) { //======否则为客户发送的温湿度查询指令,需给开发板发送指令收到命令后,反馈该客户温湿度值======= int ret; int send_num; unsigned char send_buf[CMD_MAX_LEN]; struct list_head *pos,*q; //定义遍历内核链表用的临时指针 struct cli_info *tmp = NULL; //定义结构体指针(包含内核链表成员) printf ("查询函数内tmp->num =%d\n",client_attr->num); #if 1 /*====a)遍历内核链表,查找开发板在链表中的位置,以便服务器给根据开发板给其发送查询指令 =============*/ list_for_each_safe (pos, q, client_attr->head) { tmp = list_entry (pos, struct cli_info, list); //client_attr指向遍历到的某一项结构体数据 // if(tmp->conn_addr.sin_addr.s_addr == inet_addr(BOARD_IP)) { //根据ip查找开发板在链表中的位置,send时需区其位置的fd文件描述符值 if(tmp->num == 1) { printf ("***************1_查询函数内,遍历到开发板已经连接***************\n"); break; } } #endif #if 1 //send客户传参数错误?? send_dev_board(tmp); dev_board_data.data_sequence = 0; unsigned char cmd_red = 0; cmd_red = QUERY_FEEDBACK; //查询反馈值 bzero(send_buf,CMD_MAX_LEN); printf("1_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); while( 1 != dev_board_data.data_sequence){ usleep(500); } //接收到开发板反馈的数据将为1 printf("2_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); usleep(50000); printf("3_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); list_for_each_safe (pos, q, client_attr->head) { tmp = list_entry (pos, struct cli_info, list); //client_attr指向遍历到的某一项结构体数据 // if(tmp->conn_addr.sin_addr.s_addr == inet_addr(BOARD_IP)) { //根据ip查找开发板在链表中的位置,send时需区其位置的fd文件描述符值 if(tmp->num == 1) { printf ("***********2_查询函数内,遍历到开发板已经连接*************\n"); break; } } printf("函数内,发送给开发板的fd= %d \n",tmp->conn_fd); stu_to_sti(cmd_red,p->seq,p->pac_data,send_buf); ret = send (client_attr->conn_fd,send_buf,sizeof(send_buf)+7,0); //MSG_DONTWAIT,client_attr tmp if (ret < 0) { perror ("connect"); exit (1); }else{ // printf("%d、already send,send =%s \n",send_num,p->pac_data); memset(&dev_board_data ,0, sizeof(dev_board_data)); //接收成功则将开发板发送到dev_board_data.data_sequence内的结构体数据清空 } #else //=======b)send/send,发包后若没有对方回包则继续发包SEND_NUM次======== for(send_num=0;send_num<B_SEND_NUM;send_num++) //若发送SEND_NUM次后对方仍然未接收到数据则跳出(接收失败) { send_dev_board(tmp); // usleep(B_SECOND); //发送后、等待U_SECOND us再接收数据 if(0 != dev_board_data.data_sequence){ //如果已经发送给开发板,且开发板已经反馈回来数据,则反馈给客户 unsigned char cmd_red = 0; cmd_red = QUERY_FEEDBACK; //查询反馈值 bzero(send_buf,CMD_MAX_LEN); stu_to_sti(cmd_red,p->seq,p->pac_data,send_buf); ret = send (client_attr->conn_fd,send_buf,sizeof(send_buf)+7,0); //MSG_DONTWAIT,client_attr tmp if (ret < 0) { perror ("connect"); exit (1); }else{ printf("%d、already send,send =%s \n",send_num,p->pac_data); memset(&dev_board_data ,0, sizeof(dev_board_data)); //接收成功则将开发板发送到dev_board_data.data_sequence内的结构体数据清空 return; //加一个等待客户反馈,确认接收信号会更好 } } } #endif printf("4_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); printf("*******************跳出查询函数*******************\n"); return; } //光照强度 void light_intensity_query(struct stu_info * p, cli_info * client_attr){ printf("光照强度,send->buf=%s\n",p->pac_data); return; } //查询 void query_information(struct stu_info * p, cli_info * client_attr){ //怎么发送给指定客户,封包、装包 见客户端 printf("查询,send->buf=%s\n",p->pac_data); return; } //登陆验证 void server_check_login(struct stu_info * p, cli_info * client_attr){ printf("登陆验证,send->buf=%s\n",p->pac_data); return; } //注册 void register_new_client(struct stu_info * p, cli_info * client_attr){ printf("注册,send->buf=%s\n",p->pac_data); return; } //客户退出 void client_exit(struct stu_info * p, cli_info * client_attr){ printf("客户退出,send->buf=%s\n",p->pac_data); return; } //自动查询 void auto_show(struct stu_info * p, cli_info * client_attr){ printf("客户退出,send->buf=%s\n",p->pac_data); return; } //====给开发板发包(温湿度)======== void send_dev_board(struct cli_info * p) { state_seq = (state_seq)%255+1; //发送序列号,可设置若相同则不接受 int ret =0; unsigned char buf[CMD_MAX_LEN]; // printf ("##############\n,p->conn_fd =%d\n##############\n",p->conn_fd); //????????????????? stu_to_sti_tmep(auto_query,buf); // send_content(buf); //打印??????????????????????????? ret = send (p->conn_fd,buf,sizeof(buf)+7,MSG_DONTWAIT); if (ret < 0) { perror ("connect"); exit (1); } return ; } //===开发板反馈的包存储到全局变量dev_board_data结构体==== void recv_dev_board(struct stu_info * p) { time_t cur_time; char tbuf[20]; struct tm *tm = NULL; cur_time = time (NULL); tm = localtime (&cur_time); bzero (tbuf, 20); sprintf (tbuf, "%4d-%02d-%02d %02d:%02d:%02d", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec); dev_board_data.data_sequence = 1; //有新数据则设为1 strcpy(dev_board_data.data_time , tbuf); //自动生成的时间 dev_board_data.temp_value = p->pac_data[2]; //温度值 dev_board_data.humidity_value = p->pac_data[4]; //湿度值 return ; } void send_content(unsigned char * keybuf) { printf("keybuf[0] 0x%x\n", keybuf[0]); printf("keybuf[1] 0x%x\n", keybuf[1]); printf("keybuf[2] 0x%x\n", keybuf[2]); printf("keybuf[3] 0x%x\n", keybuf[3]); printf("keybuf[4] 0x%x\n", keybuf[4]); printf("keybuf[5] 0x%x\n", keybuf[5]); printf("keybuf[6] 0x%x\n", keybuf[6]); printf("keybuf[7] 0x%x\n", keybuf[7]); } void recv_content(unsigned char * cli_buf) { printf("start_h 0: 0x%x\n", cli_buf[0]); printf("start_l 1 : 0x%x\n", cli_buf[1]); printf("lenH 2: 0x%x\n", cli_buf[2]); printf("lenlow 3: 0x%x\n", cli_buf[3]); printf("cmd 4: 0x%x\n", cli_buf[4]); printf("seq 5: %x\n", cli_buf[5]); printf("data[0] : %x\n", cli_buf[6]); printf("data[1] : %x\n", cli_buf[7]); printf("data[2] : %x\n", cli_buf[8]); printf("data[3] : %x\n", cli_buf[9]); printf("data[4] : %x\n", cli_buf[10]); printf("data[5] : %x\n", cli_buf[11]); printf("bcc 7: %x\n", cli_buf[12]); // printf("当前环境湿度为:%d,温度为:%d\n",cli_buf[8],cli_buf[10]); }
//======================================================
/***********移动互联网数据采集系统************** >功能: > 1:项目分为服务器、客户端;服务器交互对象为客户端及WATCHER开发板 > 2:客户端功能:注册、登陆、退出、删除、查询、控制及设置功能 > 3:服务端功能:客户管理、更新数据库、数据管理排序等 >设计关键词: > 内核链表(数据保存/排序/打印)、数据库(所有数据保存位置)、自定义协议(服务器与WATCHER板交互)、 > Json协议(服务器与客户端交互)、非堵塞(超时检测)、TCP协议、线程(新建线程实现)、心跳检查(2分钟) >WATCHER开发板智能控制协议主要指令: > ①RGB灯光控制指令/RGB灯光控制反馈指令,②温湿度查询指令/温湿度查询反馈指令 > ③光照强度查询指令/光照强度查询反馈指令,④时钟设置指令/时钟设置反馈指令 > ⑤闹钟设置指令/ 闹钟设置反馈指令,⑥闹钟查询指令/闹钟查询反馈指令 > ***********移动互联网数据采集系统(ser)**************/ #include"data_collect.h" /******************************************* //==============温湿度查询,将字符串转换成数据包================== void stu_to_sti(unsigned seq,unsigned char *buf) { buf[0] =0xff; //flag头部首字节,开始标志位固定为0xff buf[1] =0xff; //flag头部次字节 buf[2] =0x00; //length首字节,length为协议长度:从cmd开始到正规协议结束所占用字节数; buf[3] =0x04; //length次字节 buf[4] =0x80; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =(unsigned char)seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 buf[6] =0x02; //数据,其中data[0]为数据类型 } // 填充协议: 命令会有9个自己的开销(包含最后的'\0'字符) buf[0] = PACK_HEAD; //head buf[1] = cmd_type; // cmd_type buf[2] = 0x0; //2字节长度,高位在前,低位在后 buf[3] = cmd_len & 0xff; if (cmd_len > 255) { buf[2] = ((cmd_len & 0xff00) >> 8); // buf[3] = cmd_len & 0xff; ***********************************************/ //============(拆包,通用)将开发板上的包变成结构体====================== void string_to_stu(struct stu_info *st,unsigned char *buf) { memset(st,0,sizeof(st)); int i; printf("*0_******通用协议收包buf[0]=0x%x\n*********",buf[0]); st->flag = buf[1]; //flag头部,开始标志位固定为0xffff if(buf[0]>0) //判断flag头部高八位是否有值 st->flag += buf[0]*256; printf("*1_******通用协议收包buf[0]=0x%x\n*********",buf[0]); st->len = buf[3]; //length长度,length为协议长度:从cmd开始到正规协议结束所占用字节数; if(buf[2]>0) //判断length长度高八位是否有值 st->len += buf[2]*256; st->cmd = buf[4]; //cmd指令类型(控制/控制反馈/查询/查询反馈) st->seq = buf[5]; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 // memcpy (st->pac_data, buf+6, st->len); //客户/开发板数据,其中data[0]为数据类型 for(i=0;i < st->len; i++){ st->pac_data[6+i] = buf[2]; } st->pac_data[st->len] = '#'; st->bcc = buf[st->len+6]; //校验和:数据校验,从length到data数据的异或校验和 #if 1 printf("*******通用协议收包buf[0]=0x%x\n*********",buf[0]); printf("buf[0]=0x%x\n",buf[0]); printf("buf[1]=0x%x\n",buf[1]); printf("buf[2]=0x%x\n",buf[2]); printf("buf[3]=0x%x\n",buf[3]); printf("buf[4]=0x%x\n",buf[4]); printf("buf[5]=0x%x\n",buf[5]); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); printf("buf[len+6]=0x%x\n",(int)buf[st->len+6]); printf("len=%d\n",st->len); //????????????????????????????????????? printf("st->pac_data[0]=%c\n",st->pac_data[0]); //????????????????????????????????????? // printf("拆解后的字符串=%s\n",st->pac_data); //???????????????????? printf("st->bcc=%x\n",(int)st->bcc); #endif } //==============(封包,通用)将字符串转换成数据包========== void stu_to_sti(unsigned char cmd,unsigned char seq,unsigned char *data,unsigned char *buf) { int len =0; for(len=0;data[len] != '#'; len++); bzero(buf,len+20); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); buf[0] =0xff; //flag头部首字节,开始标志位固定为0xffff buf[1] =0xff; //flag头部次字节 if(len > 255){ buf[2] = len/256; buf[3] = len - buf[3]*256; }else{ buf[2] = 0; buf[3] = len; } buf[4] =cmd; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 // memcpy (buf+6,data,len); //客户/开发板数据,其中data[0]为数据类型 int i; for(i=0 ;data[i] == '#'; i++){ buf[6+i] = data[i]; } buf[len+6] =buf[2]^buf[3]^buf[4]^buf[5]^buf[6]; //校验和:数据校验,从length到data数据的异或校验和 // buf[len+7] = '\0'; #if 1 //测试用 printf("buf[0]=0x%x\n",buf[0]); printf("buf[1]=0x%x\n",buf[1]); printf("buf[2]=0x%x\n",buf[2]); printf("buf[3]=0x%x\n",buf[3]); printf("buf[4]=0x%x\n",buf[4]); printf("buf[5]=0x%x\n",buf[5]); printf("buf[6]=0x%x\n",buf[6]); printf("buf[7]=0x%x\n",buf[7]); printf("buf[8]=0x%x\n",buf[8]); printf("buf[9]=0x%x\n",buf[9]); printf("buf[10]=0x%x\n",buf[10]); printf("buf[11]=0x%x\n",buf[11]); printf("buf[len+6]=0x%x\n",(int)buf[len+6]); #endif } //==============(封包,开发板)温湿度查询,将字符串转换成数据包========== void stu_to_sti_tmep(unsigned char seq,unsigned char *buf) { bzero(buf,7); buf[0] =0xff; //flag头部首字节,开始标志位固定为0xffff buf[1] =0xff; //flag头部次字节 buf[2] =0x00; //length首字节,length为协议长度:从cmd开始到正规协议结束所占用字节数; buf[3] =0x04; //length次字节 buf[4] =0x80; //cmd指令类型(控制/控制反馈/查询/查询反馈) buf[5] =(unsigned char)seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 buf[6] =0x02; //数据,其中data[0]为数据类型 buf[7] =buf[2]^buf[3]^buf[4]^buf[5]^buf[6]; //校验和:数据校验,从length到data数据的异或校验和 } #if 1 //==============(拆包,开发板)温湿度查询,将字符串转换成数据包========== void string_to_stu_tmep(struct stu_info *st,unsigned char *buf) { memset(st,0,sizeof(st)); st->flag = buf[0]; //flag头部,开始标志位固定为0xffff if(buf[1]>0) //判断flag头部高八位是否有值 st->flag += buf[1]*256; st->len = buf[2]; //length长度,length为协议长度:从cmd开始到正规协议结束所占用字节数; if(buf[3]>0) //判断length长度高八位是否有值 st->len += buf[3]*256; st->cmd = buf[4]; //cmd指令类型(控制/控制反馈/查询/查询反馈) st->seq = buf[5]; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 st->pac_data[0] = buf[6]; st->pac_data[1] = buf[7]; st->pac_data[2] = buf[8]; st->pac_data[3] = buf[9]; st->pac_data[4] = buf[10]; st->pac_data[5] = buf[11]; st->bcc = buf[12]; //校验和:数据校验,从length到data数据的异或校验和 st->pac_data[6] = '#'; } #endif //温湿度 void temp_humidity_query(struct stu_info * p, cli_info * client_attr) { //======否则为客户发送的温湿度查询指令,需给开发板发送指令收到命令后,反馈该客户温湿度值======= int ret; int send_num; unsigned char send_buf[CMD_MAX_LEN]; struct list_head *pos,*q; //定义遍历内核链表用的临时指针 struct cli_info *tmp = NULL; //定义结构体指针(包含内核链表成员) printf ("查询函数内tmp->num =%d\n",client_attr->num); #if 1 /*====a)遍历内核链表,查找开发板在链表中的位置,以便服务器给根据开发板给其发送查询指令 =============*/ list_for_each_safe (pos, q, client_attr->head) { tmp = list_entry (pos, struct cli_info, list); //client_attr指向遍历到的某一项结构体数据 // if(tmp->conn_addr.sin_addr.s_addr == inet_addr(BOARD_IP)) { //根据ip查找开发板在链表中的位置,send时需区其位置的fd文件描述符值 if(tmp->num == 1) { printf ("***************1_查询函数内,遍历到开发板已经连接***************\n"); break; } } #endif #if 1 //send客户传参数错误?? send_dev_board(tmp); dev_board_data.data_sequence = 0; unsigned char cmd_red = 0; cmd_red = QUERY_FEEDBACK; //查询反馈值 bzero(send_buf,CMD_MAX_LEN); printf("1_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); while( 1 != dev_board_data.data_sequence){ usleep(500); } //接收到开发板反馈的数据将为1 printf("2_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); usleep(50000); printf("3_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); list_for_each_safe (pos, q, client_attr->head) { tmp = list_entry (pos, struct cli_info, list); //client_attr指向遍历到的某一项结构体数据 // if(tmp->conn_addr.sin_addr.s_addr == inet_addr(BOARD_IP)) { //根据ip查找开发板在链表中的位置,send时需区其位置的fd文件描述符值 if(tmp->num == 1) { printf ("***********2_查询函数内,遍历到开发板已经连接*************\n"); break; } } printf("函数内,发送给开发板的fd= %d \n",tmp->conn_fd); stu_to_sti(cmd_red,p->seq,p->pac_data,send_buf); ret = send (client_attr->conn_fd,send_buf,sizeof(send_buf)+7,0); //MSG_DONTWAIT,client_attr tmp if (ret < 0) { perror ("connect"); exit (1); }else{ // printf("%d、already send,send =%s \n",send_num,p->pac_data); memset(&dev_board_data ,0, sizeof(dev_board_data)); //接收成功则将开发板发送到dev_board_data.data_sequence内的结构体数据清空 } #else //=======b)send/send,发包后若没有对方回包则继续发包SEND_NUM次======== for(send_num=0;send_num<B_SEND_NUM;send_num++) //若发送SEND_NUM次后对方仍然未接收到数据则跳出(接收失败) { send_dev_board(tmp); // usleep(B_SECOND); //发送后、等待U_SECOND us再接收数据 if(0 != dev_board_data.data_sequence){ //如果已经发送给开发板,且开发板已经反馈回来数据,则反馈给客户 unsigned char cmd_red = 0; cmd_red = QUERY_FEEDBACK; //查询反馈值 bzero(send_buf,CMD_MAX_LEN); stu_to_sti(cmd_red,p->seq,p->pac_data,send_buf); ret = send (client_attr->conn_fd,send_buf,sizeof(send_buf)+7,0); //MSG_DONTWAIT,client_attr tmp if (ret < 0) { perror ("connect"); exit (1); }else{ printf("%d、already send,send =%s \n",send_num,p->pac_data); memset(&dev_board_data ,0, sizeof(dev_board_data)); //接收成功则将开发板发送到dev_board_data.data_sequence内的结构体数据清空 return; //加一个等待客户反馈,确认接收信号会更好 } } } #endif printf("4_查询函数内dev_board_data.data_sequence= %d \n",dev_board_data.data_sequence); printf("*******************跳出查询函数*******************\n"); return; } //光照强度 void light_intensity_query(struct stu_info * p, cli_info * client_attr){ printf("光照强度,send->buf=%s\n",p->pac_data); return; } //查询 void query_information(struct stu_info * p, cli_info * client_attr){ //怎么发送给指定客户,封包、装包 见客户端 printf("查询,send->buf=%s\n",p->pac_data); return; } //登陆验证 void server_check_login(struct stu_info * p, cli_info * client_attr){ printf("登陆验证,send->buf=%s\n",p->pac_data); return; } //注册 void register_new_client(struct stu_info * p, cli_info * client_attr){ printf("注册,send->buf=%s\n",p->pac_data); return; } //客户退出 void client_exit(struct stu_info * p, cli_info * client_attr){ printf("客户退出,send->buf=%s\n",p->pac_data); return; } //自动查询 void auto_show(struct stu_info * p, cli_info * client_attr){ printf("客户退出,send->buf=%s\n",p->pac_data); return; } //====给开发板发包(温湿度)======== void send_dev_board(struct cli_info * p) { state_seq = (state_seq)%255+1; //发送序列号,可设置若相同则不接受 int ret =0; unsigned char buf[CMD_MAX_LEN]; // printf ("##############\n,p->conn_fd =%d\n##############\n",p->conn_fd); //????????????????? stu_to_sti_tmep(auto_query,buf); // send_content(buf); //打印??????????????????????????? ret = send (p->conn_fd,buf,sizeof(buf)+7,MSG_DONTWAIT); if (ret < 0) { perror ("connect"); exit (1); } return ; } //===开发板反馈的包存储到全局变量dev_board_data结构体==== void recv_dev_board(struct stu_info * p) { time_t cur_time; char tbuf[20]; struct tm *tm = NULL; cur_time = time (NULL); tm = localtime (&cur_time); bzero (tbuf, 20); sprintf (tbuf, "%4d-%02d-%02d %02d:%02d:%02d", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec); dev_board_data.data_sequence = 1; //有新数据则设为1 strcpy(dev_board_data.data_time , tbuf); //自动生成的时间 dev_board_data.temp_value = p->pac_data[2]; //温度值 dev_board_data.humidity_value = p->pac_data[4]; //湿度值 return ; } void send_content(unsigned char * keybuf) { printf("keybuf[0] 0x%x\n", keybuf[0]); printf("keybuf[1] 0x%x\n", keybuf[1]); printf("keybuf[2] 0x%x\n", keybuf[2]); printf("keybuf[3] 0x%x\n", keybuf[3]); printf("keybuf[4] 0x%x\n", keybuf[4]); printf("keybuf[5] 0x%x\n", keybuf[5]); printf("keybuf[6] 0x%x\n", keybuf[6]); printf("keybuf[7] 0x%x\n", keybuf[7]); } void recv_content(unsigned char * cli_buf) { printf("start_h 0: 0x%x\n", cli_buf[0]); printf("start_l 1 : 0x%x\n", cli_buf[1]); printf("lenH 2: 0x%x\n", cli_buf[2]); printf("lenlow 3: 0x%x\n", cli_buf[3]); printf("cmd 4: 0x%x\n", cli_buf[4]); printf("seq 5: %x\n", cli_buf[5]); printf("data[0] : %x\n", cli_buf[6]); printf("data[1] : %x\n", cli_buf[7]); printf("data[2] : %x\n", cli_buf[8]); printf("data[3] : %x\n", cli_buf[9]); printf("data[4] : %x\n", cli_buf[10]); printf("data[5] : %x\n", cli_buf[11]); printf("bcc 7: %x\n", cli_buf[12]); // printf("当前环境湿度为:%d,温度为:%d\n",cli_buf[8],cli_buf[10]); }
//======================================================
/***********移动互联网数据采集系统************** >功能: > 1:项目分为服务器、客户端;服务器交互对象为客户端及WATCHER开发板 > 2:客户端功能:注册、登陆、退出、删除、查询、控制及设置功能 > 3:服务端功能:客户管理、更新数据库、数据管理排序等 >设计关键词: > 内核链表(数据保存/排序/打印)、数据库(所有数据保存位置)、自定义协议(服务器与WATCHER板交互)、 > Json协议(服务器与客户端交互)、非堵塞(超时检测)、TCP协议、线程(新建线程实现)、心跳检查(2分钟) >WATCHER开发板智能控制协议主要指令: > ①RGB灯光控制指令/RGB灯光控制反馈指令,②温湿度查询指令/温湿度查询反馈指令 > ③光照强度查询指令/光照强度查询反馈指令,④时钟设置指令/时钟设置反馈指令 > ⑤闹钟设置指令/ 闹钟设置反馈指令,⑥闹钟查询指令/闹钟查询反馈指令 > ***********移动互联网数据采集系统(ser)**************/ #ifndef __CHAT_H__ #define __CHAT_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h> #include <assert.h> #include <signal.h> #include <pthread.h> #include<ctype.h> //for ispunct() and isspace() #include <getopt.h> #include<time.h> //for localtime() and time() #include<sys/stat.h> #include<fcntl.h> #include "list.h" #include "list.h" #include <pthread.h> #define SERV_PORT 18880 // port #define QUEUELEN 5 // 设置同一时间最多可连接5个客户端 #define CMD_MAX_LEN 20 // 客户端输入的最大存储内容 #define U_SECOND 700000 //客户端发送后,等待U_SECOND us后接收数据 #define B_SECOND 50000 //板发送后 #define SEND_NUM 1 // 客户端发送后,若U_SECOND us后没接收到数据,再次发送数据,最大循环发送SEND_NUM次 #define B_SEND_NUM 1 // 板发送后 #define AUTO_QUERT_TIME 1 //系统自动查询温湿度(写入数据库)时间周期 #define BOARD_IP "192.168.7.100" //开发板ip"192.168.7.100" //========================functions================================================ #define TGB_LAMP_SET 1 //tgb灯 #define TEMP_HUMIDITY_QUERY 2 //温湿度 #define LIGHT_INTENSITY_QUERY 3 //光照强度 #define TIME_SET 4 //时钟设置 #define CLOCK_SET 5 //闹钟设置 #define CLOCK_QUERY 6 //闹钟查询 #define QUERY_INFORMATION 7 //查询 #define CONTROL_LAMPLIGHT 8 //控制 #define SET_ATTRIBUT 9 //设置 #define CLIENT_LOGIN 10 //登陆验证 #define CLIENT_REGISTER 11 //注册 #define CLIENT_EXIT 12 //客户退出 #define CLIENT_DELETE 13 //客户删除 #define CONTROL_FEEDBACK 0x7f //控制反馈 #define QUERY_FEEDBACK 0x8f //查询反馈 //========================functions==================== #define CLI_COMM_STR_LEN 25 #define CLIENT_ID 1000 // client id注册用户是server分配的账号起始值id++ unsigned char state_seq; //给开发板发送的序列号,头文件里面不能初始化(若不初始化有重定义清空时表示声明) struct data_collect dev_board_data; //临时存放开发板反馈的信息 unsigned int auto_query; //自动查询,每个auto_query秒打印一次; int devboard_thr_online; //判断自动查询的线程是否在线 //===============保存数据库数据(温度、湿度、光照强度)========= typedef struct data_collect{ int data_sequence; //数据序号 char data_time[20]; //数据获取时间 int temp_value; //温度值 int humidity_value; //湿度值 int intensity_value; //光照强度值 }data_collect,*pdata_collect; //=======client_registration(客户注册账号后账号存在数据库中)========= typedef struct client_registration{ char client_name[CLI_COMM_STR_LEN]; // 账号(在服务器的"数据库"唯一) char client_passwd[CLI_COMM_STR_LEN]; //密码 int client_id; //用户ID int is_online; // 在线状态 1 在线 0 不在线 int admin; //用户权限,1为管理员,0为普通用户 }_client_reg; // ===========client attr========================================== typedef struct cli_info { struct list_head list; //内核链表(文件描述符list) struct list_head *head; //内核链表(链表头指针head) int num; //新建一个链表其值加1 struct sockaddr_in conn_addr; //保存连接的IP及端口号 int conn_fd; //accept的返回的客户端的新的套接字描述符(连接时新建的线程描述符)原sockfd // pthread_t tid; //线程的描述符,unsigned long int ,printf用%lu(子线程号存储) _client_reg client_reg; //存放在数据库中的客户注册信息 }cli_info; //================函数功能的协议==================================== typedef struct { int fun_flag; //function flag void (*fun)(); // function pointer variable }proto; //================开发板数据结构,协议要求格式===================== typedef struct stu_info{ unsigned short flag; //flag头部,开始标志位固定为0xffff unsigned short len; //length长度,length为协议长度:从cmd开始到正规协议结束所占用字节数; unsigned char cmd; //cmd指令类型(控制/控制反馈/查询/查询反馈) unsigned char seq; //序号,发送者给出的序号,回复必须把序号返回发送者,以保障顺序正确性。 unsigned char pac_data[CMD_MAX_LEN]; //数据,其中data[0]为数据类型 unsigned char bcc; //校验和:数据校验,从length到data数据的异或校验和 unsigned char name[CLI_COMM_STR_LEN]; //用户名(昵称) unsigned char passwd[CLI_COMM_STR_LEN]; //密码 }stu; //====================debug============================ #define CHAT_DEBUG #ifdef CHAT_DEBUG #define DEBUG(message...) fprintf(stderr, message) #else #define DEBUG(message...) #endif //=============fun=======server.c========================== void *pthreads (void *arg); void query_information(struct stu_info * p, cli_info * client_attr); void server_check_login(struct stu_info * p, cli_info * client_attr); void register_new_client(struct stu_info * p, cli_info * client_attr); void client_exit(struct stu_info * p, cli_info * client_attr); void temp_humidity_query(struct stu_info * p, cli_info * client_attr); void light_intensity_query(struct stu_info * p, cli_info * client_attr); void auto_show(struct stu_info * p, cli_info * client_attr); void send_dev_board(struct cli_info * p); void recv_dev_board(struct stu_info * p); void *board_pthreads(); void send_content(unsigned char * keybuf); void recv_content(unsigned char * cli_buf); //=============fun=======拆包打包自定义协议========================== int send_fun(unsigned char *send_buf,unsigned char seq); int recv_fun(unsigned char *recv_buf); void string_to_stu(struct stu_info *st,unsigned char *buf); void stu_to_sti(unsigned char cmd,unsigned char seq,unsigned char *data,unsigned char *buf); void string_to_stu_tmep(struct stu_info *st,unsigned char *buf); void stu_to_sti_tmep(unsigned char seq,unsigned char *buf); #if 0 //========数据库涉及的函数=======保存数据库数据(温度、湿度、光照强度)========= extern sqlite3* open_data_sql(void); extern void creat_data_table(sqlite3 *db); extern void init_data_table(sqlite3 *db); extern void insert_data(sqlite3 *db, pdata_collect cli); extern void del_data(sqlite3 *db, int inode); extern void search_data(sqlite3 *db, int inode, pdata_collect pcli); extern void search_last_data(sqlite3 *db, pdata_collect pcli); extern void show_data(sqlite3 *db); extern void close_sql(sqlite3 *db); #endif #endif
//=====================================================
#include"sqlite.h" /* *************************************************数据库函数封装************************************************************ */ //打开数据库文件 sqlite3* open_data_sql(void) { sqlite3 *db; sqlite3_open("./data_collect.db", &db); return db; } //创建表 void creat_data_table(sqlite3 *db) { int ret; char *errmsg; char sql[SQL_SIZE] = {'\0'}; sprintf(sql, "create table data_collect_table(data_sequence int, data_time char(20), temp_value int, humidity_value int, intensity_value int)"); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret) { printf("create:%s\n", errmsg); } } //初始化表 void init_data_table(sqlite3 *db) { int ret; char *errmsg; char sql[SQL_SIZE] = {'\0'}; sprintf(sql, "insert into data_collect_table(data_sequence, data_time , temp_value, humidity_value, intensity_value) values(1, '--分隔行--', 0, 0, 0)"); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret) { printf("insert:%s\n", errmsg); exit(0); } } //根据序号(inode)删除某组数据 void del_data(sqlite3 *db, int inode) { int ret = -1; char *errmsg; char sql[SQL_SIZE] = {'\0'}; sprintf(sql, "delete from data_collect_table where data_sequence=%d", inode); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret) { printf("delete:%s\n", errmsg); } } //根据序号(inode)读取某组数据,并存储到plic指向的结构体 void search_data(sqlite3 *db, int inode, pdata_collect pcli) { int i; int ret; char *errmsg; char **result; int nrow = -1; int ncolumn = -1; // char *t_name="data_collect_table"; char sql[SQL_SIZE] = {'\0'}; int sum0 = 5, sum1 = 6, sum2 = 7, sum3 = 8, sum4 = 9; //查询client表某个账号相关的信息 //select * from table_name where [expression] sprintf(sql, "select * from data_collect_table where data_sequence=%d", inode); ret = sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg); if(SQLITE_OK != ret) { printf("search:%s\n", errmsg); } //提取数据到结构体 for(i=0; i<(nrow+1)*ncolumn; i++) { if(i < 5) { continue; } if(i == sum0) { pcli->data_sequence = atoi(result[i]); continue; } if(i == sum1) { memcpy(pcli->data_time, result[i], 19); continue; } if(i == sum2) { pcli->temp_value = atoi(result[i]); continue; } if(i == sum3) { pcli->humidity_value = atoi(result[i]); continue; } if(i == sum4) { pcli->intensity_value = atoi(result[i]); continue; } } printf("\n"); } //读取最近一次存储的数据,并存储到plic指向的结构体(待解决:该做法采用降序输出全部数据,只取第一行数据,效率较低; 会随着数据的增多而减慢运行速度) void search_last_data(sqlite3 *db, pdata_collect pcli) { int i; int ret; char *errmsg; char **result; int nrow = -1; int ncolumn = -1; // char *t_name="data_collect_table"; char sql[SQL_SIZE] = {'\0'}; int sum0 = 5, sum1 = 6, sum2 = 7, sum3 = 8, sum4 = 9; //查询client表某个账号相关的信息 //select * from table_name where [expression] sprintf(sql, "select * from data_collect_table order by data_sequence desc"); ret = sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg); if(SQLITE_OK != ret) { printf("search:%s\n", errmsg); } //提取数据到结构体 for(i=0; i<(nrow+1)*ncolumn; i++) { if(i < 5) { continue; } if(i == sum0) { pcli->data_sequence = atoi(result[i]); continue; } if(i == sum1) { memcpy(pcli->data_time, result[i], 19); continue; } if(i == sum2) { pcli->temp_value = atoi(result[i]); continue; } if(i == sum3) { pcli->humidity_value = atoi(result[i]); continue; } if(i == sum4) { pcli->intensity_value = atoi(result[i]); continue; } } printf("\n"); } //插入1组数据 void insert_data(sqlite3 *db, pdata_collect cli) { int count; int ret = -1; char *errmsg; char sql[SQL_SIZE] = {'\0'}; char buf[20]; time_t cur_time; pdata_collect client_tmp; client_tmp = (pdata_collect)malloc(sizeof(data_collect)); //更改序号data_sequence search_last_data(db, client_tmp); //printf("%d\t%s\t%d\t%d\t%d\n",client_tmp->data_sequence, client_tmp->data_time, client_tmp->temp_value, client_tmp->humidity_value, client_tmp->intensity_value); count = client_tmp->data_sequence; count++; free(client_tmp); cli->data_sequence = count; struct tm *tm = NULL; cur_time = time(NULL); /*返回从1970-01-01 00:00:00(格林威治标准时间)到现在的秒数 */ tm = localtime(&cur_time); bzero(buf,20); sprintf(buf,"%4d-%02d-%02d %02d:%02d:%02d",1900+tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour,tm->tm_min,tm->tm_sec); memcpy(cli->data_time, buf, 19); sprintf(sql, "insert into data_collect_table(data_sequence, data_time , temp_value, humidity_value, intensity_value) values(%d, '%s', %d, %d, %d)", cli->data_sequence, cli->data_time, cli->temp_value, cli->humidity_value, cli->intensity_value); ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg); if(SQLITE_OK != ret) { printf("insert:%s\n", errmsg); exit(0); } count++; //与 if(count != 1)配合,使第一次数据存储正常 } //打印数据 void show_data(sqlite3 *db) { int i; int ret; char *errmsg; char **result; int nrow = -1; int ncolumn = -1; char *t_name="data_collect_table"; char sql[SQL_SIZE] = {'\0'}; //查询client表所有信息 sprintf(sql, "select * from %s", t_name); ret = sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg); //打印查询结果 for(i=0; i<(nrow+1)*ncolumn; i++){ if(i%5 == 0) printf("\n"); printf("%s\t", result[i]); } printf("\n"); } //关闭数据库 void close_sql(sqlite3 *db) { sqlite3_close(db); } /* ******************************************************************************************************************************* */
//=====================================================
#ifndef __LIST_H #define __LIST_H /* This file is from Linux Kernel (include/linux/list.h) * and modified by simply removing hardware prefetching of list items. * Here by copyright, credits attributed to wherever they belong. * Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu) */ /* * Simple doubly linked list implementation. * * Some of the internal functions (“__xxx”) are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ /** * container_of - cast a member of a structure out to the containing structure * * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200 struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add – add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add (struct list_head *new, struct list_head *head) { __list_add (new, head, head->next); } /** * list_add_tail – add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail (struct list_head *new, struct list_head *head) { __list_add (new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del (struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } /** * list_del – deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is in an undefined state. */ static inline void list_del (struct list_head *entry) { __list_del (entry->prev, entry->next); entry->next = (void *) 0; entry->prev = (void *) 0; } /** * list_del_init – deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init (struct list_head *entry) { __list_del (entry->prev, entry->next); INIT_LIST_HEAD (entry); } /** * list_move – delete from one list and add as another’s head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add (list, head); } /** * list_move_tail – delete from one list and add as another’s tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add_tail (list, head); } /** * list_empty – tests whether a list is empty * @head: the list to test. */ static inline int list_empty (struct list_head *head) { return head->next == head; } static inline void __list_splice (struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } /** * list_splice – join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice (struct list_head *list, struct list_head *head) { if (!list_empty (list)) __list_splice (list, head); } /** * list_splice_init – join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init (struct list_head *list, struct list_head *head) { if (!list_empty (list)) { __list_splice (list, head); INIT_LIST_HEAD (list); } } /** * list_entry – get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); \ pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe – iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) #endif
//=====================================================
CC = gcc CFLAGS = -Wall -g -O OBJS = sev_data_collect cli_data_collect all : $(OBJS) sev_data_collect : sev_data_collect.c func_data_collect.c $(CC) $(CFLAGS) -o $@ $^ -lpthread cli_data_collect : cli_data_collect.c func_data_collect.c $(CC) $(CFLAGS) -o $@ $^ -lpthread clean : $(RM) $(OBJS) *.o
浙公网安备 33010602011771号