//======================================================node
/***********移動互聯網數據採集系統************** >功能: > 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"); //=====五、初始化內核鏈,設置鏈表、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、端口及必定填充區域 /*=====六、循環,內核鏈表對全部鏈接客戶端的結構體進行鏈接, 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"); //????????????????????????????????????????????????????????????????????????
//======================================================linux
/***********移動互聯網數據採集系統************** >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]); }
//======================================================sql
/***********移動互聯網數據採集系統************** >功能: > 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
//=====================================================express
#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