概括
项目功能:
1,找到树莓派不同序列号上温度的值,并读取保存
2,安装信号,通过信号判断其结束
3,参数解析,通过开始运行时的参数,进行不同的调试
4,网络连接,通过tcp/ip协议连接服务器端
5,发送消息,连接成功之后发送消息到客户端或者其他连接到的pc端
6,保存内容到数据库,若连接断开,则将取得的数据保存到本地sqlite数据库
7,拥有简单的日志系统,能记录运行时发生的事情,并具有回滚功能-->删除一定时间内的数据库文件
8,有守护进程,开始运行前判断之前运行的程序是否有关闭
9,数据可通过mqtt协议向阿里云物联网平台发送
难点(自学过程中碰到的问题):
1,tcp/ip协议
2,网络编程,本机连接到客户端或服务器端
3,tcp粘包的解决
4,无root,sudo限权下库的安装,如sqlite,mosquitto等
5,makefile的编写
6,信号的理解和安装
7,守护进程的理解和运用
8,网络连接过程中连接是否断开的判断
9,上报给阿里云的格式
10,日志系统中不定参数的使用
11,以及Linux系统的使用
12,多线程多进程的理解于应用
13,多路复用的理解与应用
14,变参函数的使用
主程序
如下:
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sqlite3.h>
#include "fun.h"
#include "pro.h"
#include "logger.h"
#include "deamon.h"
#include "mott.h"
#define PIDFILE "/tmp/.socketd.pid"
int main(int argc,char **argv)
{
int ch;
int fd=-1;
char timebuf[128];
struct Constants constants;
struct tcp_info info;
int len = sizeof(info);
char logtime_name[128];
time_t t=time(NULL);
struct Dataofget data;
int whethertemp;
time_t lasttime=0;
char pack_buf[1024];
// pack_pro packpro=packet_seg_pack;
int pack_bytes=0;
int deamon=0;
//默认60秒采集一次温度
constants.time=60;
constants.fd=-1;
//将当前打开的日志文件名字设为当前秒数
snprintf(logtime_name,sizeof(logtime_name),"./log/%ld.log",t);
if(!log_init(logtime_name, LOG_DEBUG))
{
fprintf(stdin,"open log failued\n");
return -1;
}
//将main的参数传入到结构体constants里,并分析对应作用
ch=Getdatafrommain(argc,argv,&constants);
if(ch<0)
{
printf("get data from main failed\n");
log_message(LOG_CRITICAL,__FILE__,__LINE__, "error:Get argument failed");
return -1;
}
//查看IP地址和端口号是否输入,以及是否使用moqtt连接阿里云
if(What_use(&constants))
{
printf("sorry,incorrect input format\n");
print_help(argv[0]);
log_critical("error:incorrect input format");
return -1;
}
//安装信号
install_default_signal();
//向日志文件中写入IP地址和端口号信息
GET_data((char*)constants.servip,(int)constants.port);
//check_set_program_running
#if 1
if(check_set_program_running(deamon,PIDFILE)<0)
{
goto PREND;
}
#endif
//启动成功
log_info("Program started successfully");
//日志回滚,检查上一次打开的日志文件,将过期的删除
log_roll();
while(!g_signal.stop)//判断信号,当有相对应的信号时,install_default_signal()处理相应的信号并改变g_signal.stop
{
whethertemp=0;//记录清零
//判断获取温度的时间间隔是否达到规定时间
if(TIME_interval(&lasttime,constants.time))
{
if(Get_temp(&data,&whethertemp)<0)//获取温度和序列号
{
printf("Get temp failed\n");
log_message(LOG_ERROR, __FILE__,__LINE__,"Can't get temp");
continue;
}
//将数据打包,其中有获取当前温度
pack_bytes=packet_seg_pack(&data,pack_buf,sizeof(pack_buf));
printf(" temp=%.2f\n number=%s\n time=%s\n",data.temp,data.number,data.time);
}
//等于1说明传入m相关参数,即可以mqtt协议发送数据
if(1==constants.mqtt_connect)
{
if(whethertemp)//判断是否有记录温度
{
if(mosq_send_to_server(data.temp)<0)//调用函数使用MQTT发送到阿里云端
{
log_error("Send data to alinyun failure");
printf("send data to aliyun filure\n");
return -1;
}
}
}
//若IP地址和端口都有输入,则尝试连接,且与使用mqtt无关系,两者可以并存
//连接服务器
#if 1
if(constants.fd<0)
{
if(!MYconnect(&constants))//连接服务器或者其他pc端的函数
{
printf("Connect failed\n");
}
}
if((sock_check_connect(constants.fd))<0)//判断连接是否断开
{
if(constants.fd>0)
{
log_error("socke get disconnect,terminate it and reconnect now..");
if(term_sofd(&constants)<0)//这是清除fd,让它变为-1的状态,具体情况可以看该函数内容
{
log_error("Term to socke fd failure");
}
}
}
if(constants.fd>0)
{
Query_and_Send(constants.fd);//查询数据库里的数据,若有,则发送
if(whethertemp)
{
if((Send_data(constants.fd,pack_buf,pack_bytes))<0)//发送数据,温度,序列号,时间
{
log_warning("socket send failed,save it in database now..");
Save_data(&data);//保存数据的函数
continue;
}
}
}
if(constants.fd<0)
{
if(whethertemp)
{
if(!Save_data(&data))
{
printf("Save data failed\n");
log_error("Save data failed");
return -1;
}
}
}
#endif
sleep(4);
}
PREND:
term_sofd(&constants);
log_close();
return 0;
}
安装信号并处理信号的函数
该函数的作用是及时的处理程序运行过程中接受到的各种信号并加以处理
其中处理信号的函数是一个异步的处理方式,一点想多线程处理
#include<signal.h>
#include<string.h>
#include <time.h>
#include "pro.h"
#include "logger.h"
proc_signal_t g_signal={0};
void pro_default_sighandler(int sig);
void install_default_signal(void)
{
struct sigaction sigact, signo;
log_info("Install default signal handler ");
sigemptyset(&sigact.sa_mask);
sigact.sa_flags=0;
sigact.sa_handler=pro_default_sighandler;
sigemptyset(&signo.sa_mask);
signo.sa_flags=0;
signo.sa_handler=SIG_IGN;
sigaction(SIGTERM,&sigact,0);
sigaction(SIGINT,&sigact,0);
sigaction(SIGPIPE,&sigact,0);
}
void pro_default_sighandler(int sig)
{
switch(sig)
{
case SIGINT:
log_warning("SIGINT -stopping");
g_signal.stop=1;
break;
case SIGTERM:
log_warning("SIGTERM - stopping");
g_signal.stop=1;
break;
case SIGSEGV:
log_warning("SIGSEGV - stopping");
break;
case SIGPIPE:
log_warning("SIGPIPE - stopping");
break;
default:
break;
}
}
安装信号的定义头文件
#ifndef PRH
#define PRH
typedef struct proc_signal_s
{
int signal;
unsigned stop;
} proc_signal_t;
typedef struct socket_fd_port
{
int fd;
int port;
}Sock;
extern void install_default_signal(void);
extern proc_signal_t g_signal;
#endif
守护进程的实现
这段代码主要是在检查一个程序是否已经在运行,如果运行了则记录其pid,如果没有运行则检查其是否为守护进程并启动。
该程序的实现其实是防止两次或者多次启动此程序,造成资源浪费或者冲突,产生不必要的影响
其实现原理是查询进程的id,判断是否存在
#include <sys/stat.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include<stdio.h>
#include <string.h>
#include "deamon.h"
#include "logger.h"
int check_set_program_running(int daemon,char* pidfile)
{
if(!pidfile)
{
return -1;
}
if(check_daemon_running(pidfile))
{
log_error("program already running,process exit it now");
return -2;
}
if(daemon)
{
if(set_daemon_running(pidfile)<0)
{
log_error("set program running as daemon failure");
return -3;
}
else
{
if(record_daemon_pid(pidfile)<0)
{
log_error("record program running PID failure");
return -4;
}
}
}
return 0;
}
int check_daemon_running(const char *pidfile)
{
struct stat fstatbuf;
int rv=-1;
pid_t pid=-1;
rv =stat(pidfile,&fstatbuf);//获取该文件的数据
if(rv==0)
{
printf("PID record file\"%s\" exist.\n",pidfile);
pid=get_daemon_pid(pidfile);
if(pid>0)
{
if((rv=kill(pid,0))==0)//如果返回值为0.则说明发送0信号成功,则证明目标程序存在
{
printf("program with PID[%d] seem running\n",pid);
return 1;
}
else//说明该程序不存在或出现其他故障,则移除该程序pid
{
printf("program with PID{%d} seem exit \n",pid);
remove(pidfile);
return 0;
}
}
else if(pid==0)
{
printf("can't read program PID from record file .\n");
remove(pidfile);
return 0;
}
else
{
printf("read record file %s failure,mybe program still running\n",pidfile);
return 1;
}
}
return 0;
}
int get_daemon_pid(const char* pidfile)
{
FILE *file;
pid_t pid;
char pid_buf[PID_ASCII_SIZE];
if((file=fopen(pidfile,"rb"))!=NULL)
{
(void)fgets(pid_buf,PID_ASCII_SIZE,file);//防止某些编译器报错
(void)fclose(file);
pid=atoi(pid_buf);
}
else
{
log_error("can't open PID record file %s:%s",pidfile,strerror(errno));
return -1;
}
return pid;
}
int set_daemon_running(const char* pidfile)
{
deamon(0,1);
log_info("program running as daemon PID:%d\n",getpid);
if(record_daemon_pid(pidfile)<0)
{
log_error("Record PID to file %s failure:%s",pidfile,strerror(errno));
return -2;
}
return 0;
}
void deamon(int nochdir,int noclose)
{
int rv,fd;
int i;
if(1==getppid)//getppid()函数返回当前进程的父进程的进程ID(PID)。如果getppid()返回的值等于1,这意味着当前进程的父进程是init进程(PID为1)
{
return ;
}
rv=fork();
if(rv<0)
exit(1);//表示程序遇到某种错误或异常而退出
if(rv>0)
exit(0);//表示程序成功执行完毕并退出
setsid();//这个函数通常在创建新的子进程并希望子进程拥有独立的会话和控制终端时使用。例如,在使用fork()和exec()系列函数创建新的进程时,你可能会看到setsid()的使用。这是因为新创建的进程需要与创建它的父进程完全分离,包括它们的信号环境。
if(!noclose)
{
for(i=getdtablesize();i>=0;--i)// 是一个在 UNIX 和类 UNIX 系统中可用的函数,它返回系统中可用的最大套接字(socket)表大小。这个函数通常用于检查系统能够支持的最大网络连接数。
{
close(i);
}
fd=open("/dev/null",O_RDWR);
dup(fd);
dup(fd);
}
umask(0);
if(!nochdir)
chdir("/");
return;
}
int record_daemon_pid(const char *pidfile)
{
struct stat fstatbuf;
int fd=-1;
int mode=S_IROTH| S_IXOTH | S_IRGRP|S_IXGRP|S_IRWXU;
char ip_dir[64]={0};
strncpy(ip_dir,pidfile,64);
dirname(ip_dir);
if(stat(ip_dir,&fstatbuf)<0)
{
if(mkdir(ip_dir,mode)<0)
{
log_error("can't creat %s: %s",ip_dir,strerror(errno));
return -1;
}
(void)chmod(ip_dir,mode);
}
mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
if((fd=open(pidfile,O_RDWR|O_CREAT|O_TRUNC,mode))>=0)
{
char pid[PID_ASCII_SIZE];
snprintf(pid,sizeof(pid),"%u\n",(unsigned)getpid());
write(fd,pid,strlen(pid));
close(fd);
log_debug("record PID [%u] to file %s. ",getpid(),pidfile);
}
else
{
log_error("can't creat %s:%s",pidfile,strerror(errno));
return -2;
}
return 0;
}
相关头文件
#ifndef DAEMON
#define DAEMON
#define PID_ASCII_SIZE 11
int check_set_program_running(int daemon,char* pidfile);
int check_daemon_running(const char* pidflie);
int get_daemon_pid(const char *pidfile);
int set_daemon_running(const char* pidfile);
void deamon(int nochdir,int noclose);
int record_daemon_pid(const char *pidflie);
#endif
函数实现
解释在函数内有注释
fun.c中的相关头文件
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <getopt.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include "fun.h"//自己的函数声明头文件
#include "logger.h"
#include "pro.h"
头文件fun.h的相关内容
#ifndef FUN
#define FUN
#include <time.h>
void print_help(char* progname);
int Send_data(int fd, char* data,int bytes);
struct tm * Get_nowtime();
struct Constants {
int port;
int time;
char *servip;
char *help;
int fd;
int mqtt_connect;
};
struct Dataofget
{
float temp;
char number[64];
char time[64];
};
int What_use(struct Constants *constants);
int MYconnect(struct Constants *constants);
int Get_temp(struct Dataofget *data,int *w);
int Getdatafrommain(int argc,char* argv[],struct Constants *constants);
int Save_data(struct Dataofget *data);
int Foundsql(void);
static int callback(void *data, int argc, char **argv, char **azColName) ;
int Query_and_Send(int fd);
int sock_check_connect(int fd);
int term_sofd(struct Constants *constants);
int TIME_interval(time_t *lasttime,int time);
int Convert_time(char *timebuf);
int packet_seg_pack(struct Dataofget *data,char *pack_buf,int size);
#endif
解析执行程序时输入的参数的函数
int Getdatafrommain(int argc, char *argv[], struct Constants *constants)
{
int opt;
//将结构体内容初始化,一般初始化为错误的值,便于判断
constants->port = -1;//端口号
constants->time = -1;//当前时间
constants->servip = NULL;//服务器或者其他pc端IP地址
constants->help =NULL;
constants->mqtt_connect=-1;//是否通过mqtt连接阿里云
static struct option long_options[] =
{
{"port", required_argument, 0, 'p'},
{"ip", required_argument, 0, 'i'},
{"mq", required_argument, 0, 'm'},
{"help",required_argument,0, 'h'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "p:t:i:h:m:", long_options, NULL)) != -1)
{
switch (opt)
{
case 'p':
constants->port = atoi(optarg); // 将参数转换为整数
break;
case 't':
constants->time = atoi(optarg); // 将参数转换为整数
break;
case 'i':
constants->servip = strdup(optarg); // 复制参数值作为 IP 地址
break;
case 'm':
constants->mqtt_connect=1;
break;
case '?':
default:
print_help(argv[0]);
log_message(LOG_DEBUG,__FILE__,__LINE__,"输入参数错误\n");
return 0; // 参数解析失败
}
}
return 1;
}
简陋版易懂版makefile(在无root下安装的sqlite和mosquitto)
all:first scond
gcc -o main main.c ./log/log.c -L. -I. -lfun -lsqlite3 -lmosquitto -I /home/iot14/sqlite3/include -L /home/iot14/sqlite3/lib -I./log/ -I/home/iot14/mosquite/mosquitto/usr/local/include -L/home/iot14/mosquite/mosquitto/usr/local/lib
first:
gcc -c fun.c pro.c deamon.c mott.c -L. -lmosquitto -lsqlite3 -I/home/iot14/mosquite/mosquitto/usr/local/include -L/home/iot14/mosquite/mosquitto/usr/local/lib -I/home/iot14/sqlite3/include -L/home/iot14/sqlite3/lib -I./log/
scond:
ar -rsv libfun.a *.o
clean:
rm *.a *.o main
判断结构体内各个元素是否有数据->即判断输入的参数是否符合运行
int What_use(struct Constants *constants)
{
if(constants->servip && constants->port)
{
printf(" Connect to server\n");
log_info("Connect to server");
if(1==constants->mqtt_connect)
{
printf("Use MQTT to aliyun too\n");
log_info("Use MQTT to aliyun too");
return 0;
}
return 0;
}
if(constants->mqtt_connect)
{
printf("Use MQTT to aliyun\n");
log_info(" Use MQTT to aliyun");
return 0;
}
return 1;
}
输入错误时打印输出应输入的消息的函数
void print_help(char *progname)
{
printf("-i(--ipaddr):server ip address\n");
printf("-p(--port):server port\n");
printf("-t(--time):how long (s) do you want send\n");
printf("-m(--mq):chose to wheather use mqtt\n");
}
这是连接服务器以及其他pc端的函数,相关解析在函数的注释里
int MYconnect(struct Constants *constants)
{
term_sofd(constants);//将结构体里的fd设为-1
struct sockaddr_in servaddr;
int rv=-1;
if(!constants->servip||!constants->port)//判断对应IP地址和端口号有没有
{
log_error("Don't have ip and port ");
return -1;
}
printf("the server ip : %s and port : %d\n",constants->servip,constants->port);
constants->fd=socket(AF_INET,SOCK_STREAM,0);//创建一个套接字,参数详情可以在Linux下用man手册或者自行搜索解释
if(constants->fd<0)
{
printf("The socket is failure:%s\n",strerror(errno));
log_message(LOG_ERROR, __FILE__,__LINE__,"error:%s",strerror(errno));
return -1;
}
//习惯使用前清空并设置为0
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family =AF_INET;//设置地址族为IPv4
servaddr.sin_port = htons(constants->port);//设置端口号,使用了htons()函数将端口号转换为网络字节序的无符号整数值。
inet_aton(constants->servip,&servaddr.sin_addr);//设置IP地址
//连接相关的服务器,错误的话返回-1,设置errno全局变量为错误号
rv=connect(constants->fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("Connect to server[%s:%d] failure:\n%s\n",constants->servip,constants->port,strerror(errno));
log_message(LOG_ERROR, __FILE__,__LINE__,"error:Conect to server[%s:%d] faillure:%s",constants->servip,constants->port,strerror(errno));
return -2;
}
printf("Connect is successfuly\n");
log_message(LOG_INFO, __FILE__,__LINE__,"connect successfuly");
return 1;
}
判断连接是否断开的函数
int sock_check_connect(int fd)
{
struct tcp_info info;
int len=sizeof(info);
if(fd<0)
{
return -1;
}
getsockopt(fd,IPPROTO_TCP,TCP_INFO,&info,(socklen_t*)&len);
if(TCP_CLOSE==info.tcpi_state||TCP_CLOSING==info.tcpi_state||TCP_CLOSE_WAIT==info.tcpi_state)
{
return -2;
}
return 1;
}
将结构体内的fd改为-1的函数
int term_sofd(struct Constants *constants)
{
if(!constants)
return -1;
if(constants->fd>0)
{
constants->fd =-1;
}
return 1;
}
树莓派上获取温度
int Get_temp(struct Dataofget *data,int *w)
{
static char w1_path[128]="/sys/bus/w1/devices/";
DIR *dirp=NULL;
struct dirent *direntp=NULL;
static char path2[64];
char buf[128];
int fd=-1;
char *ptr=NULL;
static int n=0;
memset(data,0,sizeof(*data));
if(n)//这个是判断第一次是否以及找到了这个地址,若是第一次找到了,就会存着,不用再重复找
{
goto NEXT;
}
dirp=opendir(w1_path);//打开一个文件
if(!dirp)
{
printf("Open dir:%s failure:%s",w1_path,strerror(errno));
log_message(LOG_ERROR, __FILE__,__LINE__,"open failure:%s",strerror(errno));
return -1;
}
while(NULL!=(direntp=readdir(dirp)))
{
if(strstr(direntp->d_name,"28-"))//寻找到有20-开头的文件,因为在树莓派上温度是这样的保存文件
{
strncpy(path2,direntp->d_name,sizeof(path2));
n=1;
}
}
if(!n)
{
printf("Can't find dir\n");
log_message(LOG_ERROR, __FILE__,__LINE__,"cant not find dir");
}
closedir(dirp);
//将目标文件名保存到同一个数组里,使打开的文件名完整
strncat(w1_path,path2,sizeof(w1_path)-strlen(w1_path));
strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));
NEXT:
strcpy(data->number,path2);//保存序列号
if((fd=open(w1_path,O_RDONLY))<0)
{
printf("Open dir %s failure:%s\n",w1_path,strerror(errno));
log_message(LOG_ERROR, __FILE__,__LINE__,"opendir all failure:%s",strerror(errno));
return -2;
}
memset(buf,0,sizeof(buf));
if((read(fd,buf,sizeof(buf)))<0)
{
printf("Read dir %s failure %s\n",w1_path,strerror(errno));
return -3;
}
// printf("What want to path is :\n%s\n",w1_path);
printf("\n");
ptr=strstr(buf,"t=");//寻找到温度
if(!ptr)
{
printf("Can not find temp data from buf\n");
log_message(LOG_ERROR, __FILE__,__LINE__,"can't find temp data");
return -4;
}
ptr +=2;//当前指针位置在"t=",向后移两位到达温度的位置
data->temp=(atof(ptr))/1000;
*w=1;//记录以及获取到温度
close(fd);
return 0;
}
树莓派上保存温度的格式
将数据打包的函数
int packet_seg_pack(struct Dataofget *data ,char *pack_buf,int size)
{
Convert_time(data->time);
memset(pack_buf,0,size);
snprintf(pack_buf,size,"%s|%s|%.3f",data->number,data->time,data->temp);
return strlen(pack_buf);
}
获取时间并保存到目标数组中去
int Convert_time(char *timebuf)
{
char timeb[128];
time_t rawtime;
struct tm *timeinfo;
memset(timeb,0,sizeof(timeb));
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(timeb, sizeof(timeb), "%Y-%m-%d %H:%M:%S", timeinfo);
strcpy(timebuf,timeb);
return 0;
}
使用MQTT协议发送信息到阿里云
#include <string.h>
#include <stdio.h>
#include "mott.h"
#include "mosquitto.h"
#include "logger.h"
#include <errno.h>
int mosq_send_to_server(float data)
{
struct mosquitto *mosq = NULL;
char databuf[64];
mosquitto_lib_init();
mosq=mosquitto_new(MQTT_CLIENTLD,"true",NULL);
if(mosquitto_username_pw_set(mosq,MQTT_USERNAME,MQTT_PASSWD)!= MOSQ_ERR_SUCCESS)
{
log_error("username and passwd set failure:%s",strerror(errno));
printf("user and pw failure:%s\n",strerror(errno));
return -1;
}
if(mosquitto_connect(mosq,MQTT_HOST,MQTT_PORT,60)!= MOSQ_ERR_SUCCESS)
{
log_error("connect to MQTT failure:%d",strerror(errno));
printf("connect failure:%s\n",strerror(errno));
return -1;
}
int len=snprintf(databuf,64,"{\"params\": {\"CurrentTemperature\": %.2f}}",data);
if(mosquitto_publish(mosq,NULL,MQTT_TOPIC,len,databuf,0,1)!=MOSQ_ERR_SUCCESS)
{
printf("publish failure:%s\n",strerror(errno));
log_error("pubulish failure:%s",strerror(errno));
return -1;
}
// printf("send data:%s seccessfully\n",databuf);
return 0;
}
相关头文件
#ifndef MOQTT
#define MOQTT
#define MQTT_HOST "iot-06z00jovaw2sruf.mqtt.iothub.aliyuncs.com"
#define MQTT_PORT 1883
#define MQTT_CLIENTLD "k0fmoG1my8x.mytemp|securemode=2,signmethod=hmacsha256,timestamp=1699370696298|"
#define MQTT_USERNAME "mytemp&k0fmoG1my8x"
#define MQTT_PASSWD "92c43812b078d05a94f112dc47ce7d76e265d9ecbbd6b1af405de742c22bf90a"
#define MQTT_TOPIC "/sys/k0fmoG1my8x/mytemp/thing/event/property/post"
int mosq_send_to_server(float data);
#endif
发送消息
int Send_data(int fd,char *data,int bytes)
{
int rv=-1;
int i=0;
int left_bytes=bytes;
if(fd<0||!data||bytes<=0)
{
printf("send data failed\n");
return -1;
}
while(bytes>0)
{
if((rv=write(fd,&data[i],left_bytes))<0)
{
log_info("socket write failure:%s",strerror(errno));
return -2;
}
else if(rv==left_bytes)
{
log_info("socket send data %d bytes over",left_bytes);
return 0;
}
else
{
i+=rv;
left_bytes-=rv;
continue;
}
}
return 0;
}
保存相关数据到数据库中
//连接断开后保存获取的数据到sqlite3数据库中
int Save_data(struct Dataofget *data)
{
sqlite3 *db;
sqlite3_stmt *stmt;
char *errMsg = 0;
int rc;
char insertSQL[1024];
rc=sqlite3_open("./sql_table/mydata.db",&db);//打开当前目录下的sql_table然后创建相关数据库表
if(rc)
{
printf("sqlite_open on Save_data failed\n");
log_message(LOG_ERROR, __FILE__,__LINE__,"error:Can't open file named mydata.db");
return -1;
}
//创建相关表
const char *createTableSQL =
"CREATE TABLE IF NOT EXISTS MyTable ("
"ID INTEGER PRIMARY KEY AUTOINCREMENT,"
"temp REAL,"
"number TEXT,"
"time TEXT);";
rc=sqlite3_exec(db, createTableSQL, 0, 0, &errMsg);//这是一条sqlite数据库执行语句
if (rc != SQLITE_OK)
{
log_message(LOG_ERROR,__FILE__,__LINE__, "Can't create table _error:%s",strerror(errno))
;
sqlite3_free(errMsg);
sqlite3_close(db);
return -2;
}
log_message(LOG_INFO, __FILE__,__LINE__,"create table successfully");
sprintf(insertSQL, "INSERT INTO MyTable (temp, number, time) VALUES ('%f', '%s','%s')", data->te
mp, data->number,data->time);
//if 0 到endif的作用和上面的效果是一样的,都是将数据存到数据库的对应位置
#if 0
const char *sql = "INSERT INTO MyTable (temp, number, time) VALUES (?,?,?);";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return rc;
}
sqlite3_bind_double(stmt, 1,data->temp);
sqlite3_bind_text(stmt, 2, data->number, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, data->time, -1, SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
printf("data inserted failure:strerror(errno)");
}
else
{
printf("Data inserted successfully\n");
}
#endif
#if 1
rc = sqlite3_exec(db, insertSQL, 0, 0, &errMsg);
if(rc != SQLITE_OK)
{
fprintf(stderr, "插入数据失败: %s %s\n", errMsg,strerror(errno));
log_message(LOG_ERROR,__FILE__,__LINE__, "Inset data failure:%s",strerror(errno));
sqlite3_free(errMsg);
sqlite3_close(db);
return-3;
}
#endif
//查询
const char *querySQL = "SELECT * FROM MyTable";
rc = sqlite3_exec(db, querySQL, callback, 0, &errMsg);
if (rc != SQLITE_OK)
{
fprintf(stderr, "查询出错: %s\n", errMsg);
log_message(LOG_ERROR, __FILE__,__LINE__,"Inquire failure:%s",strerror(errno));
sqlite3_free(errMsg);
}
sqlite3_close(db);
log_message(LOG_INFO,__FILE__,__LINE__, "Save data to sqlite table successfully");
return 1;
}
回调函数,查询数据库里的信息
static int callback (void *data, int argc, char **argv, char **azColName)
{
int i;
for (i = 0; i < argc; i++)
{
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
log_message(LOG_INFO,__FILE__,__LINE__, "callback successfully");
return 0;
}
查询数据库中的数据并发送的函数
//这是查询数据库相关表中的消息的函数
int Query_and_Send(int fd)
{
sqlite3 *db;
char *errMsg = 0;
int rc;
sqlite3_stmt *stmt;
char data[512];
const char *dropTableSQL = "DROP TABLE IF EXISTS MyTable";
const unsigned char *temp;
const unsigned char *nub;
const unsigned char *time;
int id;
int count=1;
const char *query= "SELECT * FROM MyTable";
struct Dataofget dataQ;
// pack_pro packpro=packet_seg_pack;
char pack_buf[1024];
int pack_bytes=0;
// 打开数据库
rc = sqlite3_open("./sql_table/mydata.db", &db);
if (rc)
{
printf("无法打开数据库: %s\n", sqlite3_errmsg(db));
log_message(LOG_ERROR,__FILE__,__LINE__, "Can't open mydata.db ,error:%s",strerror(errno));
return -1;
}
rc = sqlite3_prepare_v2(db, query, -1, &stmt, 0);
if(rc != SQLITE_OK)
{
log_message(LOG_ERROR,__FILE__,__LINE__, "Table no data or error:%s",strerror(errno));
return -2;
}
//遍历数据库表
while (sqlite3_step(stmt) == SQLITE_ROW)
{
memset(data, 0,sizeof(data));
id = sqlite3_column_int(stmt, 0);
dataQ.temp= sqlite3_column_double(stmt, 1);
strncpy(dataQ.number,sqlite3_column_text(stmt, 2),sizeof(dataQ.number));
strncpy(dataQ.time,sqlite3_column_text(stmt, 3),sizeof(dataQ.time));
printf("This is data when disconnected:[%d]\n",count);
pack_bytes=packet_seg_pack(&dataQ,pack_buf,sizeof(pack_buf));
Send_data(fd,pack_buf,pack_bytes);
count++;
}
rc = sqlite3_exec(db, dropTableSQL, 0, 0, &errMsg);
if(rc != SQLITE_OK)
{
printf("无法删除表格: %s\n", errMsg);
log_message(LOG_ERROR,__FILE__,__LINE__, "error:Delete table failure:%s",strerror(errno));
sqlite3_free(errMsg);
sqlite3_close(db);
}
log_message(LOG_INFO,__FILE__,__LINE__, "query and send data successfully");
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
日志系统
#include "logger.h"
#include <time.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static FILE *log_file = NULL;
//打开日志文档
int log_init(const char *log_file_path, LogLevel level)
{
log_file = fopen(log_file_path, "a");
if (!log_file)
{
printf("无法打开日志文件\n");
return -1 ;
}
return 1;
}
//向日志中写入IP地址和端口号
int GET_data(char* servip,int port)
{
time_t t = time(NULL);
char databuf[128];
snprintf(databuf,sizeof(databuf),"this connect server ip:%s port:%d",servip,port);
fprintf(log_file,"%s",databuf);
fprintf(log_file,"\n");
return 1;
}
//time,记录当前时间
const char *get_timestamp()
{
static char timestamp[20];
time_t now = time(NULL);
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&now));
return timestamp;
}
//多参数的使用
//记录写入相关事件
void log_message(LogLevel level,const char* file,int line, const char *format, ...)
{
const char *level_str[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
//用于表示一个变长参数列表。它是一个指向变长参数列表的指针,可以通过宏va_start、va_arg和va_end对变长参数列表进行访问和操作。在函数中需要接收不定数量的参数时,可以使用va_list来处理这些参数。
va_list args;
va_start(args, format);
//写入日志文档中
fprintf(log_file, "[%s]: [%s] line{%d} [%s]: ", get_timestamp(),file,line, level_str[level]);
vfprintf(log_file, format, args);
fprintf(log_file, "\n");
va_end(args);
//清空缓冲区
fflush(log_file);
}
//关闭日志文件
void log_close()
{
if (log_file)
{
fclose(log_file);
}
}
//查看日志文件日期,并把过期的删除
int log_roll()
{
DIR *dirp=NULL;
char *path="./log";
struct dirent *direntp=NULL;
dirp=opendir(path);//打开目标目录
while(NULL!=(direntp=readdir(dirp)))//读取文件名
{
if(strstr(direntp->d_name,"1"))因为日志文件打开时是以时间秒数命名的
{
Remove_if((char*)direntp->d_name);
}
}
closedir(dirp);
return 0;
}
//删除符合条件的文件,即过期的文件
int Remove_if(char* path)
{
DIR *dir=NULL;
char buf[32];
long data;
time_t t=time(NULL);
long now=(long)t;
char *result;
char *dot_position = strchr(path, '.');
char path1[64]="./log/";
int length;
strncat(path1,path,sizeof(path1));
//寻找.,因为日志文件的命名是类似于1523353.log命名的只是需要前面的数字来判断是否超过规定时间
if (dot_position != NULL)
{
length = dot_position - path;
result = (char *)malloc(length + 1);
strncpy(result, path, length);
result[length] = '\0';
strncpy(buf,result,sizeof(buf));
free(result);
}
//修改对应文件的权限,因为有的文件在打开时未赋予相关权限,从而不能删除或者修改
if (chmod(path1, S_IRWXU | S_IRWXG | S_IRWXO) != 0)
{
perror("文件权限修改失败");
exit(EXIT_FAILURE);
}
//将获得的数组转化类型
data=atol(buf);
//与1200秒相比,大于这个时间的将会被删除
if(now-data>1200)
{
if(unlink(path1)==0)
{
printf("some data of overdue was deleted\n");
}
else
printf("delet failed\n");
}
return 0;
}
#ifndef LOGGER_H
#define LOGGER_H
#include <stdio.h>
#include <stdarg.h>
typedef enum
{
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR,
LOG_CRITICAL
} LogLevel;
int GET_data(char* servip,int port);
int log_init(const char *log_file_path, LogLevel level);
void log_message(LogLevel level,const char*file,int line, const char *format, ...);
void log_close();
const char *get_timestamp();
int log_roll();
int Remove_if(char* path);
//以下的定义方法可有效避免麻烦,减少输入字符数量
#define log_debug(...) log_message(LOG_DEBUG,__FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_message(LOG_INFO,__FILE__, __LINE__, __VA_ARGS__)
#define log_warning(...) log_message(LOG_WARNING,__FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_message(LOG_ERROR,__FILE__, __LINE__, __VA_ARGS__)
#define log_critical(...) log_message(LOG_CRITICAL,__FILE__, __LINE__, __VA_ARGS__)
#endif