先看看需要实现的功能吧,服务器端比较简单,主要是客户端方面需要实现的功能比较多,就很恼火,但是花了两个星期,也是在摸爬滚打中给掌握出来了,自己的能力也有了一点提升,还是值得记录一下的。
Gitee: ds18b20获取温度
1-客户端满足要求
满足服务器端:
- =======================
- 树莓派每隔时间采样一次并上报温度到服务器,这个时间满足参数输入;
- 服务器满足端口、IP、是否后台运行参数输入设计;
- 服务器退出后,客户端采样的数据临时暂存到数据库中,并且客户端一直等待连接;
- 一旦服务器上线后,客户端自动重连并上报数据,包括数据库中暂存的数据,发送一条删除一条;
- 数据库使用sqlite3,实现增(断开时放入数据)、删(发送一条删除一条)、查(获取表格中的行数);
- 安装信号实现完美退出;
- 自己创建简单的syslog日志系统记录报错信息;
- =======================
2-客户端流程图
3-整体代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <time.h>
#include <sqlite3.h>
#include <signal.h>
#include <libgen.h>
#include <sys/types.h>
#include <libgen.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
int g_stop = 0;
typedef struct
{
char sed_del_buf[512];//暂存获取的第1条数据
int count;//获取的数据库的行数,判断数据库中是否有内容
}package;
float get_temperature();//获取温度
void get_date_time(char *buf);//获取时间
void syslog(const char* ms, ... );//日志系统,形如printf输入就可以了,会自己创建文件写进去
int socket_client_connect(char *IP, int port);//连接服务器返回sockfd
void create_table(sqlite3 *db);//创建表格
char* get_first_data(sqlite3 *db);//获取第一条数据,目的是发送给服务器
int get_line_count(sqlite3 *db);//获取数据库表格的行数
void delete_first_date(sqlite3 *db);//删除发送的内容(第一条数据)
void insert_unsent_date(sqlite3 *db, char *buf, float temper, int port);//插入未发送的内容
int inspect_socket_connected(int sockfd);//判断sockfd是否算开连接
int main (int argc, char *argv[])
{
int sockfd = -1;
int rv = -1;
int port;
char *IP;
int ch = -1;
float temper;
char buf[512];
char packet_buf[512];
int return_time = 10;
int run_daemon = -1;
sqlite3 *db = 0;
int rc = -1;
int start_time = 0;
time_t end_time;
int line_count;
char first_data_buf[512];
int whether_get_time;//这个比较关键,进入循环之后,初始化为0,获取到时间就制为1。后面判断四个方面:1.断开连接,获取到了温度;2.断开连接,没有获取到温度;3.没有断开连接,获取到温度;4.没有断开连接,没有获取到温度。四个方面来展开的。
syslog("%s :Program start running!\n", buf);
static struct option long_options[] = {
{"ipaddr", required_argument, 0, 'i'},
{"port", required_argument, 0, 'p'},
{"help", no_argument, 0, 'h'},
{"daemon", no_argument, 0, 'd'},
{"return_time", required_argument, 0, 't'},
{0, 0, 0, 0}
};
void usage( char *program_name)
{
printf("%s usage: \n", program_name);
printf("-i(--ipaddr): server IP address \n");
printf("-p(--port):server Port \n");
printf("-h(--help): For help \n");
printf("-d(--daemon): run background \n");
printf("-t(--return_time): return temper time \n");
return ;
}
while((ch = getopt_long(argc, argv, "i:p:ht:d", long_options, NULL)) != -1)
{
switch(ch)
{
case 'i':
{
IP = optarg;
break;
}
case 'p':
{
port = atoi(optarg);
break;
}
case 'h':
{
usage(argv[0]);
return 0;
}
case 't':
{
return_time = atoi(optarg);
break;
}
case 'd':
{
run_daemon = 1;
break;
}
}
}
if(run_daemon > 0)
{
daemon(0, 0);
}
void stop(int signum)
{
if(signum == SIGINT)
{
syslog("Perfect exit\n");
}
g_stop = 1;
}
signal(SIGINT, stop);
/*create database*/
rc = sqlite3_open("temper.db", &db);
if(rc)
{
syslog("open database failure:%s\n", strerror(errno));
return 0;
}
else
{
syslog("open database success!\n");
}
/*create table*/
create_table(db);
sockfd = socket_client_connect( IP, port);
if(sockfd < 0)
{
syslog("sockfd failure:%s\n", strerror(errno));
}
while(!g_stop)
{
time(&end_time);
whether_get_time = 0;
if((end_time - start_time) >= return_time)
{
memset(buf, 0, sizeof(buf));
memset(packet_buf, 0, sizeof(packet_buf));
temper = get_temperature();
get_date_time(buf);
snprintf(packet_buf, sizeof(packet_buf), "%d/%s/%f C", port, buf, temper);
whether_get_time = 1;
start_time = end_time;
}
rc = inspect_socket_connected(sockfd);
if(rc == 1)
{
if(whether_get_time == 1)
{
rv = write(sockfd, packet_buf, strlen(packet_buf) );
//printf("success\n");
if( rv < 0 )
{
syslog("server disconnected\n");
}
}
}
if(rc == 0)
{
close(sockfd);
if(whether_get_time == 1)
{
insert_unsent_date(db, buf, temper, port);
}
if(whether_get_time == 0)
{
sockfd = socket_client_connect(IP, port);
if(sockfd > 0)
{
syslog("reconnected success\n");
}
}
}
if(rc == 1)
{
line_count = get_line_count(db);
if(line_count > 0)
{
rv = snprintf(first_data_buf, sizeof(first_data_buf),"%s", get_first_data(db));
if(rv > 0)
{
rv = write(sockfd, first_data_buf, strlen(first_data_buf) );
if(rv <= 0)
{
syslog("send data from database failure:\n", strerror(errno));
close(sockfd);
continue;
}
syslog("send data from database success!\n" );
delete_first_date(db);
}
}
}
}
sqlite3_close(db);
return 0;
}
int inspect_socket_connected(int sockfd)
{
struct tcp_info info;
if (sockfd <= 0)
{
return 0;
}
int len = sizeof(info);
getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *) & len);
if ((info.tcpi_state == 1))
{
return 1;
}
else
{
return 0;
}
}
void insert_unsent_date(sqlite3 *db, char *buf, float temper, int port)
{
int rc = -1;
char *sql_insert;
sql_insert = sqlite3_mprintf("INSERT INTO temper VALUES(NULL,'%d','%s','%f')", port, buf, temper);
rc = sqlite3_exec(db, sql_insert, NULL, NULL, NULL);
if(rc != SQLITE_OK)
{
syslog("insert data failure:%s\n", strerror(errno));
}
else
syslog("insert data success\n");
}
void delete_first_date(sqlite3 *db)
{
int rc = -1;
char *sql_delete;
sql_delete = "delete from temper limit 1";
rc = sqlite3_exec(db, sql_delete, NULL, NULL, NULL);
if(rc != SQLITE_OK)
{
syslog("dalete data failure:%s\n", strerror(errno));
}
else
syslog("delete data success!\n");
}
int callback_count(void *data, int argc, char **argv, char **azColName)
{
package *sq_count;
sq_count = (package*)data;
int i;
for(i=0; i<argc; i++){
sq_count->count = atoi(argv[i] ? argv[i] : "NULL");
}
//printf("count:%d\n", sq_count->count);
return 0;
}
int get_line_count(sqlite3 *db)
{
package tmp_count;
int rc = -1;
char *sql_count;
sql_count = "select count(*) from temper";
rc = sqlite3_exec(db, sql_count, callback_count, &tmp_count, NULL);
return tmp_count.count;
}
int callback(void *data, int argc, char **argv, char **azColName)
{
int i;
int ofset = 0;
int rv = 0;
package *sq_data;
sq_data = (package*)data;
memset(sq_data -> sed_del_buf, 0, sizeof(sq_data -> sed_del_buf));
for(i=0; i<argc; i++)
{
rv = snprintf( (sq_data -> sed_del_buf) + ofset, sizeof(sq_data -> sed_del_buf) - ofset, "%s ", argv[i] ? argv[i] : "NULL");
ofset += rv;
}
}
char* get_first_data(sqlite3 *db)
{
package *tmp_data = (package*)malloc(sizeof (package));
int rc = -1;
char first_data_buf[512];
char *sql_select;
sql_select = "select *from temper limit 1";
rc = sqlite3_exec(db, sql_select, callback, tmp_data, NULL);
return tmp_data -> sed_del_buf;
}
void create_table(sqlite3 *db)
{
int rc =-1;
const char * creat = "create table temper("
"ID integer primary key autoincrement,"
"DEVICE varchar(10), "
"Data varchar(500),"
"Temperature valchar(30));";
rc = sqlite3_exec(db, creat, 0, 0, NULL );
if(rc != SQLITE_OK)
{
syslog("table already exit\n");
}
else
{
syslog("Create table failure:%s\n",strerror(errno));
}
}
float get_temperature()
{
char path[50] = "/sys/bus/w1/devices/";
int fd = -1;
int rv = -1;
DIR *dirp;
struct dirent *direntp;
char buf[128];
char *ptr;
char chip[128];
float temp;
if( (dirp = opendir(path)) == NULL)
{
syslog("Opendir failure: %s\n", strerror(errno));
return -1;
}
while((direntp = readdir(dirp)) != NULL)
{
if(strstr(direntp -> d_name, "28-"))
{
strcpy(chip, direntp -> d_name);
}
}
closedir(dirp);
strncat( path, chip, sizeof(path)-1 );
strncat( path, "/w1_slave", sizeof(path)-1 );
if((fd = open(path, O_RDONLY)) < 0)
{
syslog("Open %s failure:%s\n",path, strerror(errno));
return -2;
}
memset(buf, 0, sizeof(buf));
if((rv = read(fd, buf ,sizeof(buf))) < 0)
{
syslog("Read %s failure:%s\n",path, strerror(errno));
return -3;
}
ptr = strstr(buf, "t=") + 2;
temp = atof(ptr)/1000;
close(fd);
return temp;
}
void get_date_time(char *buf)
{
struct tm *tm_ptr;
time_t the_time;
(void)time(&the_time);
tm_ptr = gmtime(&the_time);
sprintf(buf, "%02d-%02d-%02d %02d:%02d:%02d",tm_ptr->tm_year+1900, tm_ptr->tm_mon+1, tm_ptr->tm_mday,(tm_ptr->tm_hour)+8, tm_ptr->tm_min, tm_ptr->tm_sec);
}
int socket_client_connect(char *IP, int port)
{
int sockfd = -1;
int rv = 1;
int on = 1;
struct sockaddr_in server_addr;
int server_fd = -1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
//syslog("Creat socket failure: %s\n", strerror(errno));
rv = -1;
return rv;
}
//printf("Creat sockfd[%d] successfully!\n", sockfd);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_aton(IP, &server_addr.sin_addr);
server_fd = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(server_fd < 0)
{
//syslog("Connect socket failure: %s\n", strerror(errno));
rv = -2;
goto Cleanup;
}
//printf("Connect server[%s:%d] successfully!\n", IP, sockfd);
Cleanup:
if(rv < 0)
{
close(sockfd);
}
else
{
rv = sockfd;
}
return rv;
}
/*log record*/
void syslog(const char* ms, ... )
{
char wzLog[1024];
char buffer[2049];
memset(wzLog, 0, sizeof(wzLog));
memset(buffer, 0, sizeof(buffer));
char time_buf[1024];
va_list args;
va_start(args, ms);
vsprintf( wzLog ,ms,args);
va_end(args);
get_date_time(time_buf);
snprintf(buffer, sizeof(buffer), "%s %s\n", time_buf, wzLog);
FILE* file = fopen("client_syslog.log","a+");
fwrite(buffer, 1, strlen(buffer), file);
fclose(file);
return ;
}
需要掌握的知识点或者需要注意的地方:
1.TCP/IP socket编程
2.sqlite3数据库的使用,建立数据库,建立表格,插入数据,获取数据,删除数据等
3.动态库静态库的知识,这个主要是树莓派上没有sudo权限,只能源码安装sqlite3,然后gcc的时候需要指定头文件和动态库的位置,这个想了解的看这篇:
源码安装安装sqlite3
4.makefile的使用,这个我还没实现,到时候写一篇博客记录一下
5.gitee上传文件获取文件
linux上使用git命令上传下载文件
6.函数命名简单明了,一看就懂。自定义函数已完成任务为主,尽量可以直接返回需要获取的内容
7.不懂就去网上查找,像自定义日志或者监测sockfd是否断开一般网上都有代码的,弄懂然后拿过来用,也省的自己写了。