温度采集实时上报——客户端

1. 客户端要求

  1. 树莓派上运行socket客户端程序,每隔30秒(可指定)以字符串“ID/时间/温度”形式上报采样温度,其中ID为树莓派的编号,便于服务器端区别是哪个树莓派客户端,如RPI0001/2019-01-05 11:40:30/30.0C
  2. 通过命令行参数指定服务器IP地址和端口以及间隔采样时间, getopt或getopt_long;
  3. 程序放到后台运行(daemon),并通过syslog记录程序的运行出错、调试日志;
  4. 程序能够捕捉kill信号正常退出;

2. 客户端流程图

在这里插入图片描述

3. 功能分析

  1. 建立socket通信,此处编写一个client_main()函数,与server进行connect;使用snprintf()函数进行格式化字符串,以达到指定输出格式;为了获取时间和温度样本,此处分别编写get_temper()函数和get_time()函数。
  2. 客户端连接服务器,将其打包成client_init()函数,直接调用。
  3. 通过调用long_options()函数进行命令行参数解析,并编写打印帮助信息函数print_usage()函数。
  4. 要是程序后台运行,即须调用守护进程deamon()函数,并与之配合建立log日志系统。
  5. 捕捉信号,即须安装相关信号,调用signal()函数,并编写相应回调函数signal_stop(),使程序正常退出。
  6. 再将这些调用函数的声明全部放在头文件header.h中,在主函数client_main()中包含该头文件即可。

4. 代码模块

header.h头文件

/********************************************************************************
 *      Copyright:  (C) 2019 Tang Zhiqiang<t_zhiqiang@163.com>
 *                  All rights reserved.
 *
 *       Filename:  header.h
 *    Description:  This head file is temperature project head file  
 *
 *        Version:  1.0.0(11/12/2019)
 *         Author:  Tang Zhiqiang <t_zhiqiang@163.com>
 *      ChangeLog:  1, Release initial version on "11/12/2019 07:29:02 PM"
 *                 
 ********************************************************************************/

void print_usage(char *progname);
void get_time(char *data_time);
int get_temper(float *temper);
int client_init(int port, char * serv_ip);

get_temper函数获取DS18B20采集的数据

/*********************************************************************************
 *      Copyright:  (C) 2019 Tang Zhiqiang<t_zhiqiang@163.com>
 *                  All rights reserved.
 *
 *       Filename:  get_temper.c
 *    Description:  This file is get RPI temperature in file.
 *                 
 *        Version:  1.0.0(11/09/2019)
 *         Author:  Tang Zhiqiang <t_zhiqiang@163.com>
 *      ChangeLog:  1, Release initial version on "11/09/2019 06:00:53 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include "header.h"

//#define filepath /sys/bus/w1/devices/28-041731f7c0ff/

//int get_temper(float *temper);

int get_temper(float *temper)
{
    char 	        filepath[120]="/sys/bus/w1/devices/";
    char	        f_name[50];
    char            data_array[1024];
    char            *data_p=NULL;
    struct dirent   *file=NULL;
    DIR             *dir=NULL;
    int             data_fd = -1;
    int             found = 0;
    //float           *temper;
    
    //opendir打开目录失败,返回NULL  成功:返回该路径下的所有文件和目录
    if((dir = opendir(filepath)) == NULL)
    {
        printf("opendir file failure: %s\n",strerror(errno));
        return -1;
    }

    while((file = readdir(dir)) != NULL)
    {
        //if((strcmp(file->d_name, ".", 1) == 0) || (strcmp(file->d_name, ".", 1) == 0))
        //continue;               //ignore '.' and '..' file
        if(strstr(file->d_name, "28-")) 
        {   //memset(f_name, 0, sizeof(f_name));
            strncpy(f_name, file->d_name, sizeof(f_name));       //locate reserve temperature data file path
            found = 1;      //设置found为1,即代表找到相应文件夹
            printf("reserve temperature data file path: %s\n",f_name);
        }
        //closedir(dir);
    }
    closedir(dir);
    /* found == 0; 未找到目的文件夹 */
    if(!found) 
    {
        printf("Can not find the folder\n");   
        return 0;
    }
    
    /* 找到相应文件夹后,切换至该文件夹下以获取温度数据 */
    strncat(filepath, f_name, sizeof(filepath)-strlen(filepath));  //将文件夹名连接到filepath路径后;sizeof计算数据总体大小,strlen计算当前存放字符串的大小
    strncat(filepath, "/w1_slave", sizeof(filepath)-strlen(filepath));  //将设备文件夹下存放温度的文件连接到filepath路径后

    data_fd=open(filepath, O_RDONLY);
    if(data_fd < 0) 
    {
        printf("open file failure: %s\n", strerror(errno));
        return -2;
    }
    memset(data_array, 0, sizeof(data_array));
    if(read(data_fd, data_array, sizeof(data_array)) < 0)
    {
        printf("read file failure: %s\n", strerror(errno));
        return -3;
    }
    /* data_p指针后移两个字符单位,其后即为温度数据 */
    data_p=strstr(data_array, "t=");    //strst()成功:返回第一次出现该字符串位置的指针
    data_p += 2;        //local temperature data 
    *temper=atof(data_p)/1000;    //"()" priority super "/"
    
    close(data_fd);

    return 0;
}

get_time函数获取当前系统时间,并将其返回成需要的格式

/*********************************************************************************
 *      Copyright:  (C) 2019 Tang Zhiqiang<t_zhiqiang@163.com>
 *                  All rights reserved.
 *
 *       Filename:  get_time.c
 *    Description:  This file is get current systrm time.
 *                 
 *        Version:  1.0.0(11/09/2019)
 *         Author:  Tang Zhiqiang <t_zhiqiang@163.com>
 *      ChangeLog:  1, Release initial version on "11/09/2019 05:59:44 PM"
 *                 
 ********************************************************************************/

#include <time.h>
#include <stdio.h>
#include "header.h"

//#define ip  TZQ

//void get_time(char *data_time);

void get_time(char *data_time)
{
    struct tm   *p;
    time_t      timep;
    //char        datime[50];

    time(&timep);       // 获取当前的系统时间(CUT时间)              
    p=gmtime(&timep);   //gmtime()函数参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构strcut tm*返回
    snprintf(data_time, 50, "%d-%02d-%02d  %02d:%02d:%02d", (1900+p->tm_year),  (1+p->tm_mon), p->tm_mday, (p->tm_hour+8), p->tm_min, p->tm_sec);
    //小时+8是因为我们是+8区,比CUT多8个小时
    return ;
}

client_init函数初始化客户端,连接其服务端

/*********************************************************************************
 *      Copyright:  (C) 2019 Tang Zhiqiang<t_zhiqiang@163.com>
 *                  All rights reserved.
 *
 *       Filename:  client_init.c
 *    Description:  This file is cliet initial function.
 *                 
 *        Version:  1.0.0(11/09/2019)
 *         Author:  Tang Zhiqiang <t_zhiqiang@163.com>
 *      ChangeLog:  1, Release initial version on "11/09/2019 05:57:49 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <netinet/in.h>
#include "header.h"

//int client_init(int port, char *serv_ip);

int client_init(int port, char *serv_ip)
{
    int                 con_fd = -1;
    int 		        rv = -1;
    struct sockaddr_in  serv_addr;

    con_fd=socket(AF_INET, SOCK_STREAM, 0);

    if(con_fd < 0)
    { 
        printf("Create socket failure: %s\n", strerror(errno));
        return -1;
    }
    printf("Create socket[%d] sucessfully!\n", con_fd); 
    
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(port);
    inet_aton(serv_ip, &serv_addr.sin_addr);
    
    if(con_fd >= 0)
    {
        rv=connect(con_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
        if(rv < 0)
        {
            printf("Connect to server[%s:%d] failure: %s\n", serv_ip, port, strerror(errno));
            return -2;
        }
        printf("Connect to server[%s:%d] sucessfully!\n", serv_ip, port);
    }

    return con_fd;
}

client_main函数为客户端的主函数

/*********************************************************************************
 *      Copyright:  (C) 2019 Tang Zhiqiang<t_zhiqiang@163.com>
 *                  All rights reserved.
 *
 *       Filename:  client_main.c
 *    Description:  This file socket client monitor RPI temperature.
 *                 
 *        Version:  1.0.0(10/22/2019)
 *         Author:  Tang Zhiqiang <t_zhiqiang@163.com>
 *      ChangeLog:  1, Release initial version on "10/22/2019 04:32:59 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#include <sqlite3.h>
#include "header.h"

#define     id  "t_zhiqang"  

int     g_sigstop = 0;

//void print_usage(char *progname);

void signal_stop(int signum)
{
    if(SIGTERM == signum)
    {
        printf("SIGTERM signal detected\n");
        g_sigstop = 1;
    }
}

int main(int argc, char *argv[])
{
    int                 sock_fd = -1;
    int                 rv = -1;
    struct sockaddr_in  serv_addr;
    char                *serv_ip = NULL;
    int                 serv_port = 0;
    char                buf[1024];
    int                 ch;
    char                da_time[50];                
    int                 interv_time = 0;   
    float               temper;
    int                 daemon_run = 0;
    int                 log_fd = -1;

    static struct option long_options[] = {
            {"d", no_argument, NULL, 'd'},
            {"ipaddr", required_argument, NULL, 'i'},
            {"port", required_argument, NULL, 'p'},
            {"help", no_argument, NULL, 'h'},
            {"interv_time", required_argument, NULL, 't'},
            {0, 0, 0, 0}
        };

    while((ch=getopt_long(argc, argv, "di:p:ht:", long_options, NULL)) != -1)
        {
            switch (ch) 
            {
                case 'd':
                    daemon_run = 1;
                    
                    break;
                case 'i': 
                    serv_ip=optarg;
                    break;
                case 'p': 
                    serv_port=atoi(optarg); //optarg返回的是字符串,atoi()将char转换为int型
                    break;
                case 'h': 
                    print_usage(argv[0]);
                    break;
                case 't':
                    interv_time=atoi(optarg);
                    break;
                default:
                    print_usage(argv[0]);
                    exit(0);
                    break;
            }
            
        }
        if(!serv_ip || !serv_port)
        {
            print_usage(argv[0]);
            return -1;
        }

        //openlog("socket_client_RPI", LOG_CONS | LOG_PID, 0);    //可选
        //printf("Program %s running failure: %s\n",argv[0], strerror(errno)); //将错误或调试信息写入系统自带的syslog日志
        
        signal(SIGTERM, signal_stop);   //安装signal信号
        signal(SIGPIPE, SIG_IGN);   //屏蔽SIGPIPE信号,因为当服务器主动断开socket连接后,客户端会接收到SIGPIPE信号,自动退出。
                                    //为达到服务器主动断开socket连接后,客户端重连服务器端的目的,这里必须屏蔽掉SIGPIPE信号

        //守护进程函数
        if (daemon_run)         
        {
            printf("Program %s is running at the background now\n", argv[0]);
            //创建日志系统,程序后台运行后,将所有打印信息打印在日志文件中
            log_fd = open("receive_temper.log",  O_CREAT|O_RDWR, 0666);
            if (log_fd < 0)
            {
                printf("Open the logfile failure : %s\n", strerror(errno));

                return 0;
            }
            //标准输出及标准出错重定向,重定向至日志文件
            dup2(log_fd, STDOUT_FILENO);
            dup2(log_fd, STDERR_FILENO);
            //设置deamon()函数两个参数为1
            //即保持当前目录不变,并且使标准输出及标准出错重定向仍打印输出信息,只不过此时打印信息将会全部打印至日志文件中!
            if ((daemon(1, 1)) < 0)
            {
                printf("Deamon failure : %s\n", strerror(errno));
                return 0;
            }
	} 
    while (!g_sigstop) 
    {   
            /*采样SN、时间、温度...*/  
            get_time(da_time);
            if((get_temper(&temper)) < 0)
	        {
	    	    printf("Get temperature failure: %s\n", strerror(errno));
		        continue;
	        }
            
            memset(buf, 0, sizeof(buf));
            snprintf(buf, sizeof(buf), "%s/%s/%.2f%c", id, da_time, temper, 'C');
            
            //未连接到服务器
            if(sock_fd < 0)
            {   
                //调用client_ini()函数,连接服务器
                if((sock_fd=client_init(serv_port, serv_ip)) < 0)    
                {
                    printf("connect server point  fialure: %s\n", strerror(errno));
                    continue;   //连接服务端失败,重新连接
                }
            }
            /* connect server OK!*/
            if(sock_fd >= 0)
            {
                if(write(sock_fd, buf, sizeof(buf)) < 0)
                {
                    printf("write data to server fialure: %s\n", strerror(errno));
                    close(sock_fd);
                    sock_fd = -1;
                    return -3;
                }
            }
            printf("Send messege to server sucessfully!\n");

            sleep(interv_time); //命令行参数设定上传数据间隔时间   
        }

        //closelog();
        close(sock_fd);
	    //sqlite3_close(db);

        return 0;
}

void print_usage(char *progname)
{
    printf("%s usage.\n", progname);     
    printf("%s is a socket client progname, which used to verify server and echo back string from it\n", progname);
    printf("\nMandatory arguments to long options are mandatory for short option too:\n");
    printf("-b[darmon] set program running on backgroud\n");
    printf("-t[interv_time] RPI temperature interval time\n");
    printf("-p[port] Socket client port address\n");
    printf("-i[ip] Socket client ip address\n");
    printf("-h[help] Display this help information\n");
    printf("\nExample: %s -t 30 -p 8088 -i 192.168.174.5\n", progname);
    return;
}


5. 客户端运行结果

运行结果需要在服务器运行的前提下才可以,运行结果为:
这里需要注意:环境生产可执行文件后,环境变量需要加进去,不然找不到程序header.h头文件。(这个没有写进makefile中,环境变量只是临时生效,重启失效)
在这里插入图片描述

6. 方便查看,贴出多进程的服务器运行结果:

具体可以看下面的详解
服务器详解

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值