From 805b28fc2e633bcd9c823a4b23614378ebfd50a1 Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Tue, 12 May 2026 16:52:28 +0800
Subject: [PATCH] update APUE socket project
---
project/2.socketd/booster/socket.c | 8
project/2.socketd/socket_server.c | 5
project/2.socketd/openlibs/sqlite/build.sh | 2
project/2.socketd/booster/database.h | 20 +-
project/2.socketd/booster/packet.c | 4
project/2.socketd/.gitignore | 8 +
project/2.socketd/booster/socket.h | 2
project/2.socketd/socket_client.c | 87 +++++------
project/2.socketd/booster/database.c | 262 +++++++++++++++++++++----------------
9 files changed, 222 insertions(+), 176 deletions(-)
diff --git a/project/2.socketd/.gitignore b/project/2.socketd/.gitignore
new file mode 100644
index 0000000..08603e0
--- /dev/null
+++ b/project/2.socketd/.gitignore
@@ -0,0 +1,8 @@
+# running files
+socket_server
+socket_client
+*.log
+*.db
+
+# openlibs
+sqlite-*
diff --git a/project/2.socketd/booster/database.c b/project/2.socketd/booster/database.c
index 93f386a..b114da4 100644
--- a/project/2.socketd/booster/database.c
+++ b/project/2.socketd/booster/database.c
@@ -32,7 +32,7 @@
* $db_file: sqlite database file name
* return value: <0: failure 0:ok
* */
-int database_init(const char *db_file)
+int db_open(const char *db_file)
{
char sql[SQL_COMMAND_LEN]={0};
char *errmsg = NULL;
@@ -43,48 +43,40 @@
return -1;
}
- /*+------------------------------------------+
- *| database already exist, just open it |
- *+------------------------------------------+*/
- if( 0==access(db_file, F_OK) )
+ /* open database */
+ if( SQLITE_OK != sqlite3_open_v2(db_file, &s_clidb, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL) )
{
- if( SQLITE_OK != sqlite3_open(db_file, &s_clidb) )
- {
- log_error("open database file '%s' failure\n", db_file);
- return -2;
- }
- log_info("open database file '%s' ok\n", db_file);
- return 0;
- }
-
- /*+-----------------------------------------+
- *| database not exist, create and init it |
- *+-----------------------------------------+*/
-
- if( SQLITE_OK != sqlite3_open(db_file, &s_clidb) )
- {
- log_error("create database file '%s' failure\n", db_file);
+ log_error("open/create database '%s' failed: %s\n", db_file, sqlite3_errmsg(s_clidb));
+ sqlite3_close(s_clidb);
return -2;
}
- /* SQLite continues without syncing as soon as it has handed data off to the operating system */
+ /* ---------------------------------------------
+ * SQLite performance & storage tuning
+ * --------------------------------------------- */
+
+ /* Disable synchronous for higher performance */
sqlite3_exec(s_clidb, "pragma synchronous = OFF; ", NULL, NULL, NULL);
/* enable full auto vacuum, Auto increase/decrease */
sqlite3_exec(s_clidb, "pragma auto_vacuum = 2 ; ", NULL, NULL, NULL);
- /* Create firehost table in the database */
- snprintf(sql, sizeof(sql), "CREATE TABLE %s(packet BLOB);", TABLE_NAME);
+
+ /* ---------------------------------------------
+ * Create table if not exists
+ * --------------------------------------------- */
+
+ snprintf(sql, sizeof(sql), "CREATE TABLE IF NOT EXISTS %s (packet BLOB);", TABLE_NAME);
if( SQLITE_OK != sqlite3_exec(s_clidb, sql, NULL, NULL, &errmsg) )
{
- log_error("create data_table in database file '%s' failure: %s\n", db_file, errmsg);
- sqlite3_free(errmsg); /* free errmsg */
- sqlite3_close(s_clidb); /* close databse */
- unlink(db_file); /* remove database file */
+ log_error("create table '%s' failed: %s\n", TABLE_NAME, errmsg);
+ sqlite3_free(errmsg); /* free errmsg */
+ sqlite3_close(s_clidb); /* close databse */
+ unlink(db_file); /* remove file */
return -3;
}
- log_info("create and init database file '%s' ok\n", db_file);
+ log_info("database '%s' initialized ok (table=%s)\n", db_file, TABLE_NAME);
return 0;
}
@@ -92,7 +84,7 @@
/* description: close sqlite database handler
* return value: none
*/
-void database_term(void)
+void db_close(void)
{
log_warn("close sqlite database now\n");
sqlite3_close(s_clidb);
@@ -101,164 +93,206 @@
}
-/* description: push a blob packet into database
+/*
+ * description: write a blob packet into database
* input args:
- * $pack: blob packet data address
- * $size: blob packet data bytes
- * return value: <0: failure 0:ok
+ * pack: blob packet data address
+ * size: blob packet data length
+ * return value:
+ * <0: failure
+ * 0: ok
*/
-int database_push_packet(void *pack, int size)
+int db_write(const void *pack, int size)
{
- char sql[SQL_COMMAND_LEN]={0};
- int rv = 0;
- sqlite3_stmt *stat = NULL;
+ static sqlite3_stmt *stmt = NULL;
+ int rv = 0;
- if( !pack || size<=0 )
+ if (!pack || size <= 0)
{
- log_error("%s() Invalid input arguments\n", __func__);
+ log_error("%s(): invalid input arguments\n", __func__);
return -1;
}
- if( ! s_clidb )
+ if (!s_clidb)
{
log_error("sqlite database not opened\n");
return -2;
}
- snprintf(sql, sizeof(sql), "INSERT INTO %s(packet) VALUES(?)", TABLE_NAME);
- rv = sqlite3_prepare_v2(s_clidb, sql, -1, &stat, NULL);
- if(SQLITE_OK!=rv || !stat)
+ /* Prepare statement once */
+ if (!stmt)
{
- log_error("blob add sqlite3_prepare_v2 failure\n");
- rv = -2;
- goto OUT;
+ const char *sql = "INSERT INTO " TABLE_NAME " (packet) VALUES (?);";
+ if( SQLITE_OK != sqlite3_prepare_v2(s_clidb, sql, -1, &stmt, NULL) )
+ {
+ log_error("sqlite3_prepare_v2 failed: %s\n", sqlite3_errmsg(s_clidb));
+ return -3;
+ }
}
- if( SQLITE_OK != sqlite3_bind_blob(stat, 1, pack, size, NULL) )
+ /* Bind blob */
+ if( SQLITE_OK != sqlite3_bind_blob(stmt, 1, pack, size, SQLITE_STATIC) )
{
- log_error("blob add sqlite3_bind_blob failure\n");
- rv = -3;
- goto OUT;
- }
-
- rv = sqlite3_step(stat);
- if( SQLITE_DONE!=rv && SQLITE_ROW!=rv )
- {
- log_error("blob add sqlite3_step failure\n");
+ log_error("sqlite3_bind_blob failed: %s\n", sqlite3_errmsg(s_clidb));
rv = -4;
- goto OUT;
+ goto out;
}
-OUT:
- sqlite3_finalize(stat);
+ /* Execute */
+ if( SQLITE_DONE != sqlite3_step(stmt) )
+ {
+ log_error("sqlite3_step failed: %s\n", sqlite3_errmsg(s_clidb));
+ rv = -5;
+ goto out;
+ }
- if( rv < 0 )
- log_error("add new blob packet into database failure, rv=%d\n", rv);
+ rv = 0;
+out:
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+
+ if (rv == 0)
+ log_debug("add new blob packet(size=%d) into database ok\n", size);
else
- log_info("add new blob packet into database ok\n");
+ log_error("add new blob packet(size=%d) into database failed, rv=%d\n", rv);
return rv;
}
-/* description: pop the first blob packet from database
+/*
+ * description: read the first blob packet and its rowid from database
* input args:
- * $pack: blob packet output buffer address
- * $size: blob packet output buffer size
- * $byte: blob packet bytes
- * return value: <0: failure 0:ok
+ * pack : blob output buffer
+ * size : buffer size
+ * bytes : actual blob size (output)
+ * rowid : rowid of the record (output)
+ *
+ * return value:
+ * <0 : failure
+ * 0 : ok
*/
-int database_pop_packet(void *pack, int size, int *bytes)
+int db_read(void *pack, int size, int *bytes, int *rowid)
{
- char sql[SQL_COMMAND_LEN]={0};
- int rv = 0;
- sqlite3_stmt *stat = NULL;
- const void *blob_ptr;
+ static sqlite3_stmt *stmt = NULL;
+ const void *blob_ptr;
+ int rv = 0;
- if( !pack || size<=0 )
+ if (!pack || size <= 0 || !bytes || !rowid)
{
- log_error("%s() Invalid input arguments\n", __func__);
+ log_error("%s(): invalid input arguments\n", __func__);
return -1;
}
- if( ! s_clidb )
+ if (!s_clidb)
{
log_error("sqlite database not opened\n");
return -2;
}
- /* Only query the first packet record */
- snprintf(sql, sizeof(sql), "SELECT packet FROM %s WHERE rowid = (SELECT rowid FROM %s LIMIT 1);", TABLE_NAME, TABLE_NAME);
- rv = sqlite3_prepare_v2(s_clidb, sql, -1, &stat, NULL);
- if(SQLITE_OK!=rv || !stat)
+ /* Prepare statement once */
+ if (!stmt)
{
- log_error("firehost sqlite3_prepare_v2 failure\n");
- rv = -3;
+ const char *sql = "SELECT rowid, packet FROM " TABLE_NAME " ORDER BY rowid ASC LIMIT 1;";
+ if( SQLITE_OK != sqlite3_prepare_v2(s_clidb, sql, -1, &stmt, NULL) )
+ {
+ log_error("sqlite3_prepare_v2 failed: %s\n", sqlite3_errmsg(s_clidb));
+ return -3;
+ }
+ }
+
+ /* Step to first row */
+ if ( SQLITE_ROW != sqlite3_step(stmt) )
+ {
+ rv = -4;
goto out;
}
- rv = sqlite3_step(stat);
- if( SQLITE_DONE!=rv && SQLITE_ROW!=rv )
+ /* get rowid */
+ *rowid = (int)sqlite3_column_int64(stmt, 0);
+
+ /* get blob */
+ blob_ptr = sqlite3_column_blob(stmt, 1);
+ *bytes = sqlite3_column_bytes(stmt, 1);
+
+ if (!blob_ptr || *bytes <= 0)
{
- log_error("firehost sqlite3_step failure\n");
+ log_error("invalid blob data (bytes=%d)\n", *bytes);
rv = -5;
goto out;
}
- /* 1rd argument<0> means first segement is packet */
- blob_ptr = sqlite3_column_blob(stat, 0);
- if( !blob_ptr )
+ if (*bytes > size)
{
+ log_error("blob too large (%d > %d)\n", *bytes, size);
rv = -6;
goto out;
- }
-
- *bytes = sqlite3_column_bytes(stat, 0);
-
- if( *bytes > size )
- {
- log_error("blob packet bytes[%d] larger than bufsize[%d]\n", *bytes, size);
- *bytes = 0;
- rv = -1;
}
memcpy(pack, blob_ptr, *bytes);
rv = 0;
out:
- sqlite3_finalize(stat);
+ sqlite3_reset(stmt);
return rv;
}
-/* description: remove the first blob packet from database
- * input args: none
+/*
+ * description: remove a blob packet by rowid
+ * input args:
+ * rowid : rowid of the record to delete
* return value: <0: failure 0:ok
*/
-int database_del_packet(void)
+int db_remove(int rowid)
{
- char sql[SQL_COMMAND_LEN]={0};
- char *errmsg = NULL;
+ char sql[SQL_COMMAND_LEN] = {0};
+ char *errmsg = NULL;
+ int changes = 0;
+ static int count = 0;
- if( ! s_clidb )
+ if (!s_clidb)
{
log_error("sqlite database not opened\n");
- return -2;
+ return -1;
}
- /* remove packet from db */
- memset(sql, 0, sizeof(sql));
- snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE rowid = (SELECT rowid FROM %s LIMIT 1);", TABLE_NAME, TABLE_NAME);
- if( SQLITE_OK != sqlite3_exec(s_clidb, sql, NULL, 0, &errmsg) )
+ if (rowid <= 0)
{
- log_error("delete first blob packet from database failure: %s\n", errmsg);
- sqlite3_free(errmsg);
+ log_error("invalid rowid (%d)\n", rowid);
return -2;
}
- log_warn("delete first blob packet from database ok\n");
- /* Vacuum the database */
- sqlite3_exec(s_clidb, "VACUUM;", NULL, 0, NULL);
+ /* Execute SQL */
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE rowid = %d;", TABLE_NAME, rowid);
+ if (SQLITE_OK != sqlite3_exec(s_clidb, sql, NULL, NULL, &errmsg))
+ {
+ log_error("remove packet failed (rowid=%d): %s\n",
+ rowid, errmsg ? errmsg : "unknown error");
+ sqlite3_free(errmsg);
+ return -3;
+ }
+
+ /* Check how many rows were actually deleted */
+ changes = sqlite3_changes(s_clidb);
+ if (changes == 0)
+ {
+ log_warn("no record deleted (rowid=%d)\n", rowid);
+ return -4;
+ }
+
+ log_debug("remove packet ok (rowid=%d)\n", rowid);
+
+ /*
+ * Optional: vacuum after N successful deletions
+ * WARNING: VACUUM is expensive and blocks DB access
+ */
+ if (++count >= 500)
+ {
+ log_info("trigger database VACUUM\n");
+ sqlite3_exec(s_clidb, "VACUUM;", NULL, 0, NULL);
+ count = 0;
+ }
return 0;
}
diff --git a/project/2.socketd/booster/database.h b/project/2.socketd/booster/database.h
index f64771d..3ec26e6 100644
--- a/project/2.socketd/booster/database.h
+++ b/project/2.socketd/booster/database.h
@@ -22,13 +22,13 @@
* $db_file: sqlite database file name
* return value: <0: failure 0:ok
* */
-extern int database_init(const char *db_file);
+extern int db_open(const char *db_file);
/* description: close sqlite database handler
* return value: none
*/
-extern void database_term(void);
+extern void db_close(void);
/* description: push a blob packet into database
@@ -37,24 +37,26 @@
* $size: blob packet data bytes
* return value: <0: failure 0:ok
*/
-extern int database_push_packet(void *pack, int size);
+extern int db_write(const void *pack, int size);
/* description: pop the first blob packet from database
* input args:
- * $pack: blob packet output buffer address
- * $size: blob packet output buffer size
- * $byte: blob packet bytes
+ * $pack : blob packet output buffer address
+ * $size : blob packet output buffer size
+ * $byte : blob packet bytes
+ * $rowid: rowid of the record
* return value: <0: failure 0:ok
*/
-extern int database_pop_packet(void *pack, int size, int *bytes);
+extern int db_read(void *pack, int size, int *bytes, int *rowid);
/* description: remove the first blob packet from database
- * input args: none
+ * input args:
+ * $rowid: rowid of the record
* return value: <0: failure 0:ok
*/
-extern int database_del_packet(void);
+extern int db_remove(int rowid);
#endif /* ----- #ifndef _DATABASE_H_ ----- */
diff --git a/project/2.socketd/booster/packet.c b/project/2.socketd/booster/packet.c
index da448dd..c495d15 100644
--- a/project/2.socketd/booster/packet.c
+++ b/project/2.socketd/booster/packet.c
@@ -34,13 +34,15 @@
int get_time(struct tm *ptm)
{
+ time_t now;
+
if( !ptm )
{
log_error("Invalid input arugments\n");
return -1;
}
- time_t now = time(NULL);
+ now = time(NULL);
localtime_r(&now, ptm);
return 0;
diff --git a/project/2.socketd/booster/socket.c b/project/2.socketd/booster/socket.c
index de82a00..c14f347 100644
--- a/project/2.socketd/booster/socket.c
+++ b/project/2.socketd/booster/socket.c
@@ -210,7 +210,7 @@
* $sock: socket context pointer
* return value: 1: connected 0:disconnected
*/
-int socket_connected(socket_t *sock)
+int socket_check(socket_t *sock)
{
struct tcp_info info;
int len=sizeof(info);
@@ -270,6 +270,7 @@
if( !sock )
return -1;
+ /* close and clear previous socket */
socket_term(sock);
/*+--------------------------------------------------+
@@ -284,7 +285,7 @@
/* If $host is a valid IP address, then don't use name resolution */
if( inet_aton(sock->host, &inaddr) )
{
- //log_info("%s is a valid IP address, don't use domain name resolution.\n", sock->host);
+ //log_info("Hostname %s is an IP address, so domain resolution can be skipped.\n", sock->host);
hints.ai_flags |= AI_NUMERICHOST;
}
@@ -326,6 +327,8 @@
if( 0 == rv )
{
sock->fd = sockfd;
+ sock->connected = 1;
+ socket_set_keepalive(sockfd, 10, 3);
log_info("Connect to server[%s:%d] on fd[%d] successfully!\n", sock->host, sock->port, sockfd);
break;
}
@@ -590,6 +593,7 @@
return -2;
}
+ /* If keepintvl and keepcnt are both 0, the system default configuration will be used. */
if(keepintvl || keepcnt)
{
/*
diff --git a/project/2.socketd/booster/socket.h b/project/2.socketd/booster/socket.h
index 186f390..9b4e4da 100644
--- a/project/2.socketd/booster/socket.h
+++ b/project/2.socketd/booster/socket.h
@@ -94,7 +94,7 @@
* $sock: socket context pointer
* return value: 1: connected 0:disconnected
*/
-extern int socket_connected(socket_t *sock);
+extern int socket_check(socket_t *sock);
/* description: socket client connect to server
* input args:
diff --git a/project/2.socketd/openlibs/sqlite/build.sh b/project/2.socketd/openlibs/sqlite/build.sh
index 60b9623..ec715d7 100755
--- a/project/2.socketd/openlibs/sqlite/build.sh
+++ b/project/2.socketd/openlibs/sqlite/build.sh
@@ -6,7 +6,7 @@
PACK_SUFIX=tar.gz
# LingYun source code FTP server
-LY_FTP=http://weike-iot.com:2211/src/
+LY_FTP=http://master.weike-iot.com:2211/src/
# library download URL address
LIB_URL=$LY_FTP
diff --git a/project/2.socketd/socket_client.c b/project/2.socketd/socket_client.c
index 3830c28..49ebe78 100644
--- a/project/2.socketd/socket_client.c
+++ b/project/2.socketd/socket_client.c
@@ -27,7 +27,7 @@
#include "database.h"
#define PROG_VERSION "v1.0.0"
-#define DAEMON_PIDFILE "/tmp/.socketc.pid"
+#define DAEMON_PIDFILE "/tmp/socketc.pid"
static void print_usage(char *progname)
{
@@ -46,15 +46,15 @@
return;
}
-int check_sample_time(time_t *last_time, int interval);
-
int main (int argc, char **argv)
{
char *progname=NULL;
int daemon = 1;
int rv;
+ int rowid = 0;
+ time_t now;
- char *logfile="sock_client.log";
+ char *logfile="/tmp/socketc.log";
int loglevel=LOG_LEVEL_INFO;
int logsize=10; /* logfile size max to 10K */
@@ -119,7 +119,6 @@
default:
break;
}
-
}
if( !serverip || !port )
@@ -128,28 +127,36 @@
return 0;
}
+ /* initial socket context */
+ socket_init(&sock, serverip, port);
+
/* open logger system */
if( log_open(logfile, loglevel, logsize, THREAD_LOCK_NONE) < 0 )
{
- fprintf(stderr, "Initial log system failed\n");
+ fprintf(stderr, "Initial logger failed\n");
return 1;
}
- /* install signal proc handler */
- install_default_signal();
-
- /* check program already running or not, if already running then exit, or set running as daemon */
- if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
- goto cleanup;
-
- log_info("socket client start running.\n");
-
- if( database_init("sock_client.db") < 0 )
+ /* open database for cache data */
+ if( db_open("sock_client.db") < 0 )
{
- return 2;
+ fprintf(stderr, "Initial database failed\n");
+ rv = 2;
+ goto cleanup;
}
- socket_init(&sock, serverip, port);
+ /* check whether the program is running; if not, start it in the background. */
+ if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
+ {
+ fprintf(stderr, "Program is already running. Exiting.\n");
+ rv = 3;
+ goto cleanup;
+ }
+
+ /* install signal handler */
+ install_default_signal();
+
+ log_info("socket client start running.\n");
while( ! g_signal.stop )
{
@@ -158,11 +165,10 @@
* +----------------------------------+*/
sample_flag = 0; /* clear sample flag */
+ time(&now); /* get current time */
- if( check_sample_time(&last_time, interval) )
+ if( difftime(now, last_time) >= interval )
{
- log_debug("start DS18B20 sample termperature\n");
-
if( (rv=ds18b20_get_temperature(&pack_info.temper)) < 0 )
{
log_error("DS18B20 sample temperature failure, rv=%d\n", rv);
@@ -176,6 +182,7 @@
pack_bytes = pack_proc(&pack_info, (uint8_t *)pack_buf, sizeof(pack_buf));
log_dump(LOG_LEVEL_DEBUG, NULL, pack_buf, pack_bytes);
+ last_time = now; /* update last sample time */
sample_flag = 1; /* set sample flag */
}
@@ -184,7 +191,7 @@
* +---------------------------------+*/
/* start connect to server if not connected */
- if( !socket_connected(&sock) )
+ if( !socket_check(&sock) )
{
socket_connect(&sock);
}
@@ -192,13 +199,14 @@
/* +-------------------------------+
* | socket disconnect |
* +-------------------------------+*/
- if( !socket_connected(&sock) )
+ if( !sock.connected )
{
if( sample_flag )
{
- database_push_packet(pack_buf, pack_bytes);
+ db_write(pack_buf, pack_bytes);
}
+ /* Bypass the data send procedure for socket disconnect */
continue;
}
@@ -213,13 +221,13 @@
if( socket_send(&sock, pack_buf, pack_bytes) < 0 )
{
log_warn("socket send sample packet failure, save it in database now.\n");
- database_push_packet(pack_buf, pack_bytes);
+ db_write(pack_buf, pack_bytes);
continue;
}
}
- /* socket send packet in database */
- if( !database_pop_packet(pack_buf, sizeof(pack_buf), &pack_bytes) )
+ /* socket send cache packet */
+ if( !db_read(pack_buf, sizeof(pack_buf), &pack_bytes, &rowid) )
{
log_debug("socket send database packet bytes[%d]: %s\n", pack_bytes, pack_buf);
if( socket_send(&sock, pack_buf, pack_bytes) < 0 )
@@ -230,34 +238,21 @@
else
{
log_warn("socket send database packet okay, remove it from database now.\n");
- database_del_packet();
+ db_remove(rowid);
}
}
msleep(5);
}
+ rv = 0;
+
cleanup:
socket_term(&sock);
- database_term();
- unlink(DAEMON_PIDFILE);
+ db_close();
log_close();
+ unlink(DAEMON_PIDFILE);
- return 0;
+ return rv;
}
-int check_sample_time(time_t *last_time, int interval)
-{
- int need = 0; /* no need sample now */
- time_t now;
-
- time(&now);
-
- if( difftime(now, *last_time)>interval )
- {
- need = 1; /* need sample now */
- *last_time = now;
- }
-
- return need;
-}
diff --git a/project/2.socketd/socket_server.c b/project/2.socketd/socket_server.c
index 100233e..e83ddbc 100644
--- a/project/2.socketd/socket_server.c
+++ b/project/2.socketd/socket_server.c
@@ -25,7 +25,7 @@
#include "packet.h"
#define PROG_VERSION "1.0.0"
-#define DAEMON_PIDFILE "/tmp/.sockets.pid"
+#define DAEMON_PIDFILE "/tmp/sockets.pid"
#define MAX_EVENTS 512
/* Every client need get a stand alone buffer to save TLV packet data */
@@ -63,7 +63,7 @@
int i = 0;
int rv = 0;
- char *logfile="sock_server.log";
+ char *logfile="/tmp/sockets.log";
int loglevel=LOG_LEVEL_INFO;
int logsize=10; /* logfile size max to 10K */
@@ -252,6 +252,7 @@
cleanup:
socket_term(&sock);
+ unlink(DAEMON_PIDFILE);
log_close();
return 0;
}
--
Gitblit v1.9.1