C语言基于Socket和SQLite的智能家居服务器实现
C语言基于Socket和SQLite的智能家居服务器实现/n/n本文介绍如何使用C语言、Socket编程和SQLite数据库实现一个简单的智能家居服务器。/n/n### 服务器端代码/n/nc/n#include <stdio.h>/n#include <stdlib.h>/n#include <string.h>/n#include <unistd.h>/n#include <sys/socket.h>/n#include <arpa/inet.h>/n#include <sqlite3.h>/n#include <pthread.h>/n#include <signal.h>/n/n#define MAX_BUFFER_SIZE 1024/n/n// 设备状态结构体/ntypedef struct {/n int uid;/n char device_name[10];/n char device_state[10];/n char value[10];/n char mode[10];/n} Status;/n/n// 服务器上下文结构体,包含服务器相关信息/ntypedef struct {/n int sockfd;/n struct sockaddr_in client_addr;/n socklen_t client_addr_len;/n sqlite3 *db;/n pthread_mutex_t mutex;/n} ServerContext;/n/n/n// 处理客户端请求/nvoid handleClientRequest(ServerContext *context) {/n char buffer[MAX_BUFFER_SIZE];/n int userid;/n/n // 接收客户端发送的userid/n ssize_t recvSize = recv(context->sockfd, &userid, sizeof(int), 0);/n if (recvSize == -1) {/n perror('recv');/n return;/n }/n userid = ntohl(userid); // 将网络字节序转换为主机字节序/n printf('Received userid: %d//n', userid);/n/n // 查询数据库获取设备状态信息/n char sql[100];/n snprintf(sql, sizeof(sql), 'SELECT * FROM Status WHERE uid = %d', userid);/n sqlite3_stmt *stmt;/n int rc = sqlite3_prepare_v2(context->db, sql, -1, &stmt, NULL);/n if (rc != SQLITE_OK) {/n fprintf(stderr, 'Failed to execute statement: %s//n', sqlite3_errmsg(context->db));/n return;/n }/n/n Status status;/n rc = sqlite3_step(stmt);/n if (rc == SQLITE_ROW) {/n status.uid = sqlite3_column_int(stmt, 1);/n strncpy(status.device_name, sqlite3_column_text(stmt, 2), sizeof(status.device_name)-1);/n strncpy(status.device_state, sqlite3_column_text(stmt, 3), sizeof(status.device_state)-1);/n strncpy(status.value, sqlite3_column_text(stmt, 4), sizeof(status.value)-1);/n strncpy(status.mode, sqlite3_column_text(stmt, 5), sizeof(status.mode)-1);/n } else if (rc == SQLITE_DONE) {/n fprintf(stderr, 'No data found for user %d//n', userid);/n sqlite3_finalize(stmt);/n return;/n } else {/n fprintf(stderr, 'Failed to execute statement: %s//n', sqlite3_errmsg(context->db));/n sqlite3_finalize(stmt);/n return;/n }/n sqlite3_finalize(stmt);/n/n // 分析设备状态并生成建议/n char suggestion[MAX_BUFFER_SIZE];/n if (strcmp(status.device_name, '空调') == 0) {/n int temperature = atoi(status.value);/n if (temperature < 24) {/n snprintf(suggestion, sizeof(suggestion), '空调温度过低,建议提高温度至26℃');/n } else {/n snprintf(suggestion, sizeof(suggestion), '空调温度正常');/n }/n } else if (strcmp(status.device_name, '加湿器') == 0) {/n int humidity = atoi(status.value);/n if (humidity < 40 || humidity > 70) {/n snprintf(suggestion, sizeof(suggestion), '加湿器湿度过高或过低,建议调整加湿器湿度');/n } else {/n snprintf(suggestion, sizeof(suggestion), '加湿器湿度正常');/n }/n } else {/n snprintf(suggestion, sizeof(suggestion), '设备状态未知');/n }/n/n // 向客户端发送建议/n ssize_t sendSize = send(context->sockfd, suggestion, strlen(suggestion), 0);/n if (sendSize == -1) {/n perror('send');/n return;/n }/n}/n/n// 处理客户端连接/nvoid *handleClientConnection(void *arg) {/n ServerContext *context = (ServerContext *)arg;/n int clientSockfd = accept(context->sockfd, (struct sockaddr *)&context->client_addr, &context->client_addr_len);/n if (clientSockfd == -1) {/n perror('accept');/n return NULL;/n }/n/n context->sockfd = clientSockfd;/n handleClientRequest(context);/n/n close(clientSockfd);/n return NULL;/n}/n/nint main() {/n // 创建套接字/n int sockfd = socket(AF_INET, SOCK_STREAM, 0);/n if (sockfd == -1) {/n perror('socket');/n return 1;/n }/n/n // 设置服务器地址/n struct sockaddr_in server_addr;/n server_addr.sin_family = AF_INET;/n server_addr.sin_addr.s_addr = INADDR_ANY;/n server_addr.sin_port = htons(12345);/n/n // 绑定套接字到服务器地址/n if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {/n perror('bind');/n close(sockfd);/n return 1;/n }/n/n // 监听连接请求/n if (listen(sockfd, 5) == -1) {/n perror('listen');/n close(sockfd);/n return 1;/n }/n/n // 打开数据库/n sqlite3 *db;/n int rc = sqlite3_open('../SmartHome/Client/Smarthome_Client/database/database.db', &db);/n if (rc != SQLITE_OK) {/n fprintf(stderr, 'Failed to open database: %s//n', sqlite3_errmsg(db));/n close(sockfd);/n return 1;/n }/n/n // 初始化服务器上下文/n ServerContext context;/n context.sockfd = sockfd;/n context.client_addr_len = sizeof(context.client_addr);/n context.db = db;/n pthread_mutex_init(&context.mutex, NULL);/n/n // 处理客户端连接/n pthread_t tid;/n while(1){/n if (pthread_create(&tid, NULL, handleClientConnection, &context) != 0) {/n perror('pthread_create');/n return 1;/n }/n pthread_detach(tid); // 设置线程分离/n }/n/n // 关闭数据库/n rc = sqlite3_close(db);/n if (rc != SQLITE_OK) {/n fprintf(stderr, 'Failed to close database: %s//n', sqlite3_errmsg(db));/n return 1;/n }/n/n // 关闭套接字/n if (close(sockfd) == -1) {/n perror('close');/n return 1;/n }/n/n pthread_mutex_destroy(&context.mutex);/n return 0;/n}/n/n/n### 客户端代码/n/ncpp/n#include 'procession.h'/n#include 'ui_procession.h'/n#include <QAbstractSocket>/n#include <QDebug>/n/nProcession::Procession(int userid,QWidget *parent) :/n QWidget(parent),/n ui(new Ui::Procession),/n userid(userid)/n{/n ui->setupUi(this);/n processionWidget();/n m_socket = new QTcpSocket(this);/n connect(ui->connectBtn, &QPushButton::clicked, this, &Procession::on_pushButton_clicked);/n connect(m_socket, &QTcpSocket::readyRead, this, &Procession::readyRead);/n connect(m_socket, &QTcpSocket::connected, this, &Procession::connected);/n connect(m_socket, &QTcpSocket::disconnected,this,&Procession::disconnected);/n connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &Procession::displayError);/n}/n/nProcession::~Procession()/n{/n delete ui;/n}/n/nvoid Procession::processionWidget()/n{/n setWindowTitle('服务器通信');/n setAutoFillBackground(true);/n QPalette palette=this->palette();/n QPixmap pixmap(':user/image/image/net.jpg');/n palette.setBrush(QPalette::Window, QBrush(pixmap));/n setPalette(palette);/n setFixedSize(600,400);/n}/nvoid Procession::on_pushButton_clicked()/n{/n if (m_socket->state() == QAbstractSocket::ConnectedState) {/n ui->message->append('已经连接上服务器');/n return;/n }/n/n QString ip = ui->IP->text();/n QString port = ui->port->text();/n/n if (ip.isEmpty() || port.isEmpty()) {/n ui->message->clear();/n ui->message->append('请输入有效的IP地址和端口号');/n return;/n }/n/n ui->message->clear();/n ui->message->append('正在连接中...');/n m_socket->connectToHost(ip, static_cast<quint16>(ui->port->text().toInt()));/n}/n/nvoid Procession::connected()/n{/n ui->message->append('连接成功');/n // 发送userid给服务器/n QByteArray block;/n QDataStream out(&block, QIODevice::WriteOnly);/n out << this->userid;/n if (m_socket->write(block) == -1) {/n ui->message->append('发送userid失败');/n m_socket->close();/n }/n}/n/nvoid Procession::disconnected()/n{/n ui->message->append('连接失败');/n m_socket->close();/n}/n/nvoid Procession::readyRead()/n{/n QByteArray data = m_socket->readAll();/n if (data.isEmpty()) {/n ui->message->append('读取的数据为空');/n return;/n }/n // 解析服务器返回的数据/n QDataStream in(&data, QIODevice::ReadOnly);/n QString suggestion;/n in >> suggestion; // 使用重载的 >> 运算符来读取数据/n if (in.status() != QDataStream::Ok) {/n ui->message->append('解析服务器返回的数据失败');/n return;/n }/n ui->message->append(suggestion);/n m_socket->close();/n}/n/nvoid Procession::displayError(QAbstractSocket::SocketError error)/n{/n QString errorMessage;/n switch (error) {/n case QAbstractSocket::ConnectionRefusedError:/n errorMessage = '连接被拒绝';/n break;/n case QAbstractSocket::RemoteHostClosedError:/n errorMessage = '远程主机关闭';/n break;/n case QAbstractSocket::HostNotFoundError:/n errorMessage = '未找到主机';/n break;/n case QAbstractSocket::SocketTimeoutError:/n errorMessage = '连接超时';/n break;/n case QAbstractSocket::NetworkError:/n errorMessage = '网络错误';/n break;/n default:/n errorMessage = '未知错误';/n break;/n }/n ui->message->append('连接失败:' + errorMessage);/n}/n/n/n### 代码说明/n/n1. 服务器端代码使用C语言编写,使用了Socket编程和SQLite数据库。/n2. 服务器端监听指定的端口,等待客户端连接。/n3. 客户端连接成功后,将自己的用户ID发送给服务器。/n4. 服务器端接收到用户ID后,查询数据库获取该用户的设备状态信息。/n5. 服务器端根据设备状态信息生成建议,并发送给客户端。/n6. 客户端接收到建议后,将其显示在界面上。/n/n### 运行结果/n/n服务器端成功运行后,会监听指定的端口。客户端连接到服务器后,会发送自己的用户ID。服务器端根据用户ID查询数据库,并返回相应的建议给客户端。/n/n### 问题解决/n/n在代码中,出现过段错误(Segmentation fault)的问题,这是由于访问了无效的内存地址导致的。经过分析,原因是在处理客户端连接时,没有正确地传递ServerContext结构体的指针给handleClientConnection函数。/n/n解决方法是将handleClientConnection函数的参数改为ServerContext指针类型,并在调用handleClientConnection函数时,传递正确的指针。/n/n修改后的代码如下:/n/nc/n// 修改handleClientConnection函数的参数为ServerContext指针类型/nvoid *handleClientConnection(ServerContext *context) {/n // .../n}/n/nint main() {/n // .../n/n // 创建ServerContext结构体变量context/n ServerContext context;/n // .../n/n // 在调用handleClientConnection函数时,传递正确的指针/n pthread_t tid;/n while(1){/n if (pthread_create(&tid, NULL, handleClientConnection, &context) != 0) {/n perror('pthread_create');/n return 1;/n }/n pthread_detach(tid); // 设置线程分离/n }/n/n // .../n}/n/n/n另外,还需要注意在handleClientConnection函数中,需要将pthread/_create函数的返回值作为线程的退出状态返回,以便在主线程中调用pthread/_join函数等待子线程退出。/n/n修改后的代码如下:/n/nc/nvoid *handleClientConnection(ServerContext *context) {/n // .../n return NULL; // 返回线程的退出状态/n}/n/nint main() {/n // .../n/n pthread_t tid;/n pthread_create(&tid, NULL, handleClientConnection, &context);/n/n // 等待子线程退出/n pthread_join(tid, NULL);/n/n // .../n}/n/n/n这样,应该能够解决段错误的问题。/n
原文地址: http://www.cveoy.top/t/topic/fCgW 著作权归作者所有。请勿转载和采集!