博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
网络socket编程之温度实时监控上报项目(客户端)
阅读量:3934 次
发布时间:2019-05-23

本文共 6873 字,大约阅读时间需要 22 分钟。

前言:放寒假之前收到了老师布置的的这个项目实战,能够实现一个项目完整的功能。之前上课有学习一些socket网络编程,Linux下文件I/O,多进程、多线程、多路复用等知识,至于之后项目中用到的sqlite3数据库是之后自己通过网上了解到的。这些知识都是零零散散的,这个项目刚好拿来练习,可以把知识点都串联起来。其实寒假前就已经基本完成了所有的功能,一直没有写一篇博客,都快忘却了,所以得写一篇博客回忆复习一下

首先大致介绍一下这个项目和思路

该项目由客户端和服务器端组成,树莓派上通过1-Wire协议连接DS18B20温度传感器,每隔一定时间采样上报给服务器程序,服务器端收到来自各个客户端的数据之后保存到数据库中。程序放到后台运行,并通过syslog记录程序的运行出错、调试日志。程序能够捕捉信号正常退出。

客户端代码

首先需要画出流程图,得出编程思路,再进行编程。流程图如下:

在这里插入图片描述

1.通过域名解析,将树莓派的域名解析成IP地址,通过上网查询,dns函数可以实现该功能。

void dns(char *domain_name,char **ip)  {     struct hostent       *server_name=NULL;      server_name=gethostbyname(domain_name);     inet_ntop(server_name->h_addrtype,server_name->h_addr,*ip,32);  }
  1. 参数解析,由于可能出现多个服务器的情况,通过参数解析可以访问到指定的树莓派服务器

    opt=getopt(argc,argv,"xy");         if(opt!='x' && opt!='y')         {                printf("argument parsing failure:%s\n",strerror(errno));             return -1;          }            if(opt=='x')         {                dns("xxxxxxxx.com",&servip);         }            if(opt=='y')         {                dns("yyyyyyyy.com",&servip);         }

3.使用socket函数初始化

server_fd = socket( AF_INET, SOCK_STREAM, 0);    //返回一个文件描述符server_fd            if(server_fd < 0)            {                printf("Fail to create a client socket [%d]: %s\n", server_fd, strerror(errno) );                return -1;            }                printf("creat a client socket[%d] successufully!\n",server_fd);                             memset(&servaddr, 0, sizeof(servaddr));                servaddr.sin_family = AF_INET;             if(opt == 'x')                     //参数解析制定端口             {             servaddr.sin_port = htons(port=9033);             printf("Get temperature from [%d]xxxxxxxx.com...\n",port);             }             if(opt == 'y')             {             servaddr.sin_port =htons(port=8034);             printf("Get temperature form [%d]yyyyyyyy.com...\n",port);             }

4.while循环

while(!g_stop)       //g_stop是一个信号,收到信号时跳出while循环,程序结束             {                 getdatatime(datime);     //获得时间的函数                 ds18b20_get_temperature(&temp);     //采样温度的函数                 snprintf(buf,sizeof(buf),"%s/%s/%f℃ ",user,datime,temp);                 //下面四个函数是sqlite3数据库的数据存储                 db=db_connect(db_name);       //连接到数据库                 db_create();                  //创建表                 db_insert();                   //插入数据                 db_select();                  //显示表中数据              if(rv<0)             {                rv=connect(server_fd,(struct sockaddr *)&servaddr,sizeof(servaddr));//连接到服务器                if(rv<0)                {                 printf("connect to server[%s:%d] failure: %s\n",servip,port,strerror(errno));                 close(rv);                 break;                }                 printf("connect to server[%s:%d] successfully!\n",servip,port);             }                 rv=write(server_fd,buf,strlen(buf));   //将字符串buf中的内容写给服务器                 if(rv<0)            {                    printf("write to server by server_fd [%d] failure:%s\n",server_fd,strerror(errno));                    return -1;                    break;            }                    printf("write to server by server_fd[%d] [%s]successfully!\n",server_fd,buf);                      rv=read(server_fd,buf,sizeof(buf));     //从服务器中读取内容            if(rv<0)            {                    printf("read data from server by server[%d] failure:%s\n",server_fd,strerror(errno));                    return -1;                    break;            }            else if(rv==0)            {                 printf("server_fd [%d] get disconnect\n",server_fd);                 return -1;                 break;            }            else if(rv>0)            {                printf("read %d bytes data from server:%s\n",rv,buf);            }               sleep(60);    //每隔一分钟循环一次         }

5.安装信号

void sig_handler(int signum){  if(SIGTERM==signum)  {     printf("SIGTERM signal detected\n");     g_stop=1;  }  else if(SIGALRM==signum)  {     printf("SIGALRM signal detected\n");     g_stop=1;  }  else if(SIGINT==signum)  {     printf("SIGINT signal detected\n");     g_stop=1;  }}void signal_code(int signum){       if(SIGBUS==signum)       {               printf("SIGBUS signal detected\n");       }       else if(SIGILL==signum)       {            printf("SIGILL signal detected\n");       }       else if(SIGSEGV==signum)       {            printf("SIGSEGV signal detected\n");       }       exit(1);}

调用

signal(SIGTERM,sig_handler);    //kill命令默认发送的信号,程序终止     signal(SIGALRM,sig_handler);    //alarm()系统调用发送的信号     signal(SIGINT,sig_handler);     //Ctrl+C按键终止程序的信号     signal(SIGBUS,signal_code);    //运行非本CPU相关编译器编译的程序     signal(SIGILL,signal_code);    //强制杀死程序信号,任何程序都不可以捕捉该信号     signal(SIGSEGV,signal_code);    //段错误系统给程序发送的信号

6.获取时间函数

int getdatatime(char *datime){  time_t seconds;  struct tm *pTM;  time(&seconds);  pTM=localtime(&seconds);  sprintf(datime,"%04d-%02d-%02d  %02d:%02d:%02d\n",pTM->tm_year+1900,pTM->tm_mon+1,pTM->tm_mday,pTM->tm_hour,pTM->tm_min,pTM->tm_sec);  return 0;}

7.温度采样函数,该树莓派上的温度传感器ds18b20测量到的温度会通过字符串的形式被记录到一个特定的文件夹下面,文件内容会随着温度的变化而变化。这时我们需要用到Linux下文件I/O的相关操作去获取温度值。

int ds18b20_get_temperature(float *temp){        char     w1_path[50]="/sys/bus/w1/devices/";     //文件夹的路径        char     chip[20];        char     buf[128];        DIR      *dirp;        struct dirent  *direntp;        int   fd=-1;        char   *ptr;        int    found=0;  if(!temp)  {    return -1;  }  if((dirp=opendir(w1_path))==NULL)  //打开相关文件夹  {    printf("opendir error:%s\n",strerror(errno));    return -2;  }  while((direntp=readdir(dirp))!=NULL) if(strstr(direntp->d_name,"28-"))   //通过readdir函数找到文件名是“”28-“开头的文件夹”    {      strcpy(chip,direntp->d_name);      found=1;      break;    }  }  closedir(dirp);  if(!found)  {    printf("can not find bs18b20 in %s\n",w1_path);    return -3;  }    strncat(w1_path,chip,sizeof(w1_path)-strlen(w1_path));    strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));    if((fd=open(w1_path,O_RDONLY))<0)      //打开记录温度的文件    {      printf("open %s error:%s\n",w1_path,strerror(errno));      return -4;    }     if(read(fd,buf,sizeof(buf))<0)          //读取到文件中指定位置的温度字符串    {      printf("read %s error:%s\n",w1_path,strerror(errno));      return -5;    }    ptr=strstr(buf,"t=");    if(!ptr)    {      printf("error:can not get temperature\n");      return -6;    }    ptr+=2;    *temp=atof(ptr)/1000.0;     //强制类型转换,除以1000是温度的真实值    close(fd);    return 0;}

8.有关数据库的函数(这一块也不详解了,之后会单独写篇博客讲解,其实数据库应该放到服务器端的,哈哈)

int callback(void *notused,int argc,char **argv,char **name){    for(int i=0;i

以上是客户端的相关代码,具体可以参考本人码云上的代码

在Linux下运行结果:
在这里插入图片描述
进入sqlite3 数据库:
在这里插入图片描述

项目需要准备:

1.保证pc端通过公网ip或者域名在SecureCRT上远程登录
2.在树莓派上安装好sqlite3数据库和相关函数接口
sudo apt-get install sqlite3和sudo apt-get install libsqlite3-dev
3.提前在树莓派连接的路由器上开通相应的端口号(防止端口号重复)

后序:因为是本人第一次独自完成项目,难免会有很多瑕疵和不足,希望以后有更多锻炼的机会吧。之后也会把项目服务器端的相关代码和知识点介绍一下。

转载地址:http://hyhgn.baihongyu.com/

你可能感兴趣的文章
Git config alias 设置命令别名
查看>>
Git 追踪内容详解
查看>>
Git 打包文件详解
查看>>
C 数据对齐算法
查看>>
Openssl 移植
查看>>
wireless tools 移植和使用
查看>>
libnl1.1.4 移植
查看>>
udhcpc 移植和使用
查看>>
libnl3.2 移植
查看>>
Uboot 使用串口Kermit协议传输文件
查看>>
wpa_supplicant 移植和使用
查看>>
hostapd 移植和使用
查看>>
windows下go 环境安装
查看>>
ubuntu13.04源更新
查看>>
netbeans 应用
查看>>
php 编码及加密
查看>>
专注核心
查看>>
职业规划
查看>>
高效会议
查看>>
不一样的产品思维
查看>>