当前位置: 首页 > web >正文

Linux套接字+Sqlite实例:客户端-服务器应用程序教程

本文将详细介绍如何创建一个基于客户端-服务器架构的应用程序,实现用户注册、登录、单词查询以及历史记录查询。该应用通过TCP套接字进行客户端和服务器之间的通信,并通过SQLite数据库进行用户和查询记录的管理。教程会逐步解析客户端和服务器端的实现,特别是与SQLite数据库的交互部分。


1. 系统概述

本系统包括两部分:客户端服务器端。客户端通过与服务器通信,执行不同的操作,如注册、登录、查询单词和查询历史记录。服务器端负责接收和处理这些请求,操作数据库(SQLite)来管理用户信息和查询记录。

1.1 功能列表

  • 用户注册:客户端提供用户名和密码,服务器将其存储到数据库中。
  • 用户登录:客户端提供用户名和密码,服务器验证其正确性。
  • 单词查询:客户端输入查询单词,服务器返回单词的释义。
  • 历史记录查询:客户端查看之前查询过的单词。

2. 服务器端实现

2.1 初始化与数据库连接

服务器端负责接收来自客户端的请求并处理相关操作。我们首先需要打开数据库并创建服务器套接字。

2.1.1 打开SQLite数据库

在服务器端,使用 sqlite3_open 打开一个名为 my.db 的SQLite数据库:

if(sqlite3_open(DATABASE, &db) != SQLITE_OK)
{printf("%s\n", sqlite3_errmsg(db));return -1;
}
else
{printf("open DATABASE success!\n");
}

这里我们定义了一个常量 DATABASE,它指定了数据库的文件名(my.db)。如果数据库文件不存在,SQLite会自动创建一个新的数据库。

2.2 服务器主程序

服务器端通过 socket() 创建一个TCP套接字,然后绑定端口并监听客户端连接。

2.2.1 创建套接字并绑定端口
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{perror("fail to socket.\n");return -1;
}bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // 服务器 IP
serveraddr.sin_port = htons(atoi(argv[2]));       // 服务器端口号if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{perror("fail to bind.\n");return -1;
}if(listen(sockfd, 5) < 0)
{perror("fail to listen");
}
  1. socket() 创建套接字。
  2. bind() 绑定IP地址和端口号。
  3. listen() 使套接字处于监听状态,准备接收客户端连接。
2.2.2 处理客户端请求

服务器使用 accept() 接受客户端的连接请求并为每个客户端创建一个新的子进程来处理请求。子进程调用 do_client() 函数来处理客户端的请求。

if((acceptfd = accept(sockfd, NULL, NULL)) < 0)
{perror("fail to accept");return -1;
}if((pid = fork()) < 0)
{perror("fail to fork!");return -1;
}
else if(pid == 0)  // 子进程
{close(sockfd);  // 子进程关闭监听套接字do_client(acceptfd, db);  // 处理客户端请求
}
else  // 父进程
{close(acceptfd);  // 父进程关闭连接套接字
}

fork() 子进程中,关闭监听套接字,并调用 do_client() 函数进行实际处理。


2.3 处理具体操作

服务器通过 do_client() 根据消息类型来分发任务(注册、登录、查询单词、查询历史记录)。

2.3.1 处理注册请求

服务器接收到注册请求后,将客户端传来的用户名和密码插入到数据库 usr 表中。

void do_register(int acceptfd, MSG *msg, sqlite3 *db)
{char sql[512];char *errmsg;snprintf(sql, sizeof(sql), "insert into usr values('%s', '%s');", msg->name, msg->data);if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){strcpy(msg->data, "usr name already exist.");}else{strcpy(msg->data, "OK!");}send(acceptfd, msg, sizeof(MSG), 0);
}
  • 如果用户名已存在,返回 "usr name already exist."
  • 否则,插入新用户信息,并返回 "OK!" 表示成功。
2.3.2 处理登录请求

登录请求的处理方式是检查数据库中是否存在匹配的用户名和密码。如果存在,返回 "OK";否则返回 "usr/passwd wrong."

int do_login(int acceptfd, MSG *msg , sqlite3 *db)
{char sql[512] = {};char *errmsg;int nrow, ncloumn;char **resultp;sprintf(sql, "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data);if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK){return -1;}if(nrow == 1){strcpy(msg->data, "OK");send(acceptfd, msg, sizeof(MSG), 0);return 1;}else{strcpy(msg->data, "usr/passwd wrong.");send(acceptfd, msg, sizeof(MSG), 0);}return 0;
}

2.3.3 处理单词查询请求

服务器根据客户端请求的单词进行查询。如果找到了单词,就返回其释义;如果没有找到,返回 "Not found!"

int do_query(int acceptfd, MSG *msg , sqlite3 *db)
{char word[64];int found = 0;char date[128] = {};char sql[512] = {};strcpy(word, msg->data); // 获取要查询的单词found = do_searchword(acceptfd, msg, word);if(found == 1){get_date(date); // 获取当前时间sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){return -1;}}else{strcpy(msg->data, "Not found!");}send(acceptfd, msg, sizeof(MSG), 0);return 0;
}

2.3.4 处理历史记录查询

服务器从 record 表中获取客户端的查询历史记录,并逐条发送给客户端。

int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{char sql[512] = {};sprintf(sql, "select * from record where name = '%s'", msg->name);if(sqlite3_exec(db, sql, history_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK){printf("%s\n", errmsg);}msg->data[0] = '\0'; // 发送结束标志send(acceptfd, msg, sizeof(MSG), 0);return 0;
}

3. 客户端实现

客户端通过TCP套接字连接到服务器,发送请求并接收响应。

3.1 客户端功能

3.1.1 用户注册

客户端通过输入用户名和密码,发送注册请求到服务器,服务器返回注册结果。

int do_register(int sockfd, MSG *msg)
{msg->type = R;printf("Input name:");      scanf("%s", msg->name);getchar();printf("Input passwd:");    scanf("%s", msg->data);if(send(sockfd, msg, sizeof(MSG), 0) < 0){printf("fail to send.\n");return -1;}if(recv(sockfd, msg, sizeof(MSG), 0) < 0){printf("Fail to recv.\n");return -1;}printf("%s\n", msg->data);return 0;
}

3.1.2 用户登录

登录功能要求用户输入用户名和密码,客户端向服务器发送登录请求,服务器返回验证结果。

int do_login(int sockfd, MSG *msg)
{msg->type = L;printf("Input name:");      scanf("%s", msg->name);getchar();printf("Input passwd:");    scanf("%s", msg->data);if(send(sockfd, msg, sizeof(MSG), 0) < 0){printf("fail to send.\n");return -1;}if(recv(sockfd, msg, sizeof(MSG), 0) < 0){printf("Fail to recv.\n");return -1;}if(strncmp(msg->data, "OK", 3) == 0){printf("Login ok!\n");return 1;}else {printf("%s\n", msg->data);}return 0;
}

3.2 其他功能实现

类似地,客户端实现了查询单词和查询历史记录的功能。通过输入单词或历史记录命令,客户端发送请求并接收服务器返回的结果。


4. 总结

通过这篇教程,我们介绍了如何实现一个基于TCP套接字的客户端-服务器应用,并结合SQLite数据库来存储用户信息和查询历史。关键的实现步骤包括:

  • 使用TCP套接字进行客户端和服务器之间的通信。
  • 使用SQLite数据库管理用户注册、登录、单词查询和历史记录。
  • 客户端和服务器通过结构化的消息(MSG 结构体)进行数据交换。

通过这种方式,我们构建了一个简单但功能完善的客户端-服务器系统。

http://www.xdnf.cn/news/1958.html

相关文章:

  • 科技赋能建筑新未来:中建海龙模块化建筑产品入选中国建筑首批产业化推广产品
  • URP-利用矩阵在Shader中实现物体的平移和缩放
  • 【Google上包前APK自检】
  • Mac中 “XX”文件已损坏,无法打开 解决方案
  • shell命令一
  • uniapp中检查版本,提示升级app,安卓下载apk,ios跳转应用商店
  • ubiquant比赛系列——用docker准备ubipoker开发环境
  • volatile缓存可见性实现原理
  • 支付场景下,乐观锁的实现(简洁版)
  • AiFlutter 低代码平台介绍
  • hadoop-3.3.5.tar.gz 镜像
  • Hi3516CV608 超高清智慧视觉 SoC 芯片 可提供开发资料
  • Python 快速获取Excel工作表名称
  • Easysearch 基础运维扫盲指南:从 HTTP 到 HTTPS、认证与安全访问全解析
  • 侧扫声呐概述
  • Ollama 常见命令速览:本地大模型管理指南
  • arcpy列表函数的应用(2)
  • 数据作为新生产要素,如何实现价值变现?
  • 向量数据库实践:存储和检索向量数据
  • Python实验三
  • 论文学习:《聚类矩阵正则化指导的层次图池化》
  • Java多线程
  • 2.5 桥梁桥面系及附属结构施工
  • kafka课后总结
  • Spring @Transactional 自调用问题深度解析
  • 【Unity 与c++通信】Unity与c++通信注意事项,参数传递
  • websheet之 自定义函数
  • 成都种业博览会预登记火热进行中,6月8日-9日成都世纪城新国际会展中心与您不见不散!
  • [密码学实战]商用密码产品密钥体系架构:从服务器密码机到动态口令系统
  • vue前端SSE工具库|EventSource 替代方案推荐|PUSDN平行宇宙软件开发者网