sqlite数据库通过网络通信的TCP框架进行交互的简单示例代码
实现的功能
-
创建一个名为"student.db"的数据库,通过网络通信的TCP框架,实现在客户端发出增(insert) 删(delete) 改(update) 查(query) 的请求,增删改查整个的操作是在服务器端实现的,在客户端发出增删改查的请求,在服务器端返回增删改查的结果给客户端,然后在客户端显示。
-
要求在以上网络编程,数据库的基础上加上并发的操作,完成单服务器对多个客户端的支持。
此代码只是在本地进行测试的代码,如有不同需求,更改所需的网络端口号和ip地址即可。
代码实现
- 服务端代码 server.c
为实现并发的操作,在服务端接收到客户端的连接请求后进行子进程的创建,创建子进程后在子进程中关闭监听套接字,只处理客户端的请求,父进程关闭客户端套接字,继续监听新的客户端连接请求。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define DATABASE "student.db"
#define N 128
#define M 1024
#define PORT 8888
#define MAX_CLIENTS 10
//函数声明
int client_handle(int client_socket, sqlite3 *db);
int do_insert(sqlite3 *db, char *request);
int do_delete(sqlite3 *db, char *request);
int do_update(sqlite3 *db, char *request);
int do_query(sqlite3 *db, char *request, char *response_buffer);
int callback(void *arg, int f_num, char **f_value, char **f_name);
int do_query1(sqlite3 *db, char *request, char *response_buffer);
int main(int argc, const char *argv[])
{
int server_socket, client_socket;
struct sockaddr_in server_address, client_address;
socklen_t address_len = sizeof(client_address);
sqlite3 *db;
char *errmsg;
//创建数据库连接
if (sqlite3_open(DATABASE, &db) != SQLITE_OK){
printf("%s\n", sqlite3_errmsg(db));
return -1;
}else{
printf("Open DATABASE success.\n");
}
//创建表格
if(sqlite3_exec(db, "create table if not exists stu(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, sex TEXT, score INTEGER);",
NULL, NULL, &errmsg) != SQLITE_OK)
{
printf("%s\n", errmsg);
}
else
{
printf("Create or open table success.\n");
}
//创建服务器套接字
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket creation failed:\n");
exit(0);
}
//设置通信地址结构体
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
server_address.sin_addr.s_addr = htons(0);
// 地址快速重用
int flag = 1;
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0){
perror("setsockopt failed.\n");
exit(0);
}
//绑定套接字
if ( (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address))) < 0){
perror("Bind failed: \n");
exit(0);
}
//监听连接请求
if (listen(server_socket, MAX_CLIENTS) < 0){
perror("Listen failed: \n");
exit(0);
}
//接收并处理客户端连接请求
while (1){
//接收客户端连接请求
if ( (client_socket = accept(server_socket, (struct sockaddr *)&client_address, &address_len)) < 0){
perror("Accept failed: \n");
exit(0);
}
printf("[%s:%d] connection success.\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));
// 创建子进程处理客户端请求
pid_t pid = fork();
if (pid < 0){
perror("Fork failed: \n");
exit(0);
}else if (pid == 0){
int flag = 1;
//子进程中处理客户端请求
close(server_socket);
while (flag){
if (client_handle(client_socket, db) < 0)
flag = 0;
}
close(client_socket);
exit(0);
}else{
//父进程中继续监听新的连接请求
close(client_socket);
}
}
//关闭数据库连接
sqlite3_close(db);
return 0;
}
//处理客户端请求
int client_handle(int client_socket, sqlite3 *db){
char request[N] = {};
char response[M] = {};
//接收请求类型
int request_type;
if (read(client_socket, &request_type, sizeof(int)) < 0){
perror("Read failed: \n");
exit(0);
}
printf("request_type = %d\n", request_type);
//接收客户端请求数据
if (read(client_socket, request, sizeof(request)) < 0){
perror("Read failed: \n");
exit(0);
}
//根据请求类型进行处理
switch(request_type){
case 1:
do_insert(db, request);
strcpy(response, "Insert done.\n");
break;
case 2:
do_delete(db, request);
strcpy(response, "Delete done.\n");
break;
case 3:
do_update(db, request);
strcpy(response, "Update done.\n");
break;
case 4:
do_query(db, request, response);
break;
case 5:
do_query1(db, request, response);
break;
default:
printf("Invalid request.\n");
strcpy(response, "Invalid request.\n");
break;
}
//将操作结果发送给客户端
if (write(client_socket, response, strlen(response)) < 0){
perror("Write failed: \n");
exit(0);
}
//检测客户端是否断开连接
int bytes_read = recv(client_socket, request, sizeof(request), MSG_PEEK);
if (bytes_read == 0) {
printf("Client disconnected.\n");
return -1;
}
return 0;
}
// 插入学生信息
int do_insert(sqlite3 *db, char *request) {
char name[32] = {};
char sex[32] = {};
int score;
char sql[N] = {};
char *errmsg;
sscanf(request, "%s %s %d", name, sex, &score);
sprintf(sql, "insert into stu (name, sex, score) values('%s', '%s', %d);", name, sex, score);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
printf("%s\n", errmsg);
}
return 0;
}
// 删除学生信息
int do_delete(sqlite3 *db, char *request){
int id;
char sql[N] = {};
char *errmsg;
sscanf(request, "%d", &id);
sprintf(sql, "delete from stu where id = %d;", id);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){
printf("%s\n", errmsg);
}
return 0;
}
// 更新学生信息
int do_update(sqlite3 *db, char *request){
int id;
int score;
char sql[N] = {};
char *errmsg;
sscanf(request, "%d %d", &id, &score);
sprintf(sql, "update stu set score = %d where id = %d;", score, id);
if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){
printf("%s\n", errmsg);
}
return 0;
}
//查询学生成绩
int do_query(sqlite3 *db, char *request, char *response_buffer){
char *errmsg;
char name[32] = {};
char sql[N] = {};
sscanf(request, "%s", name);
sprintf(sql, "select score from stu where name = '%s';", name);
if (sqlite3_exec(db, sql, callback, response_buffer, &errmsg) != SQLITE_OK){
printf("%s\n", errmsg);
}
return 0;
}
// 查询回调函数
int callback(void *arg, int f_num, char **f_value, char **f_name){
int i;
int score_index = -1; // 成绩字段的索引
char *response_buffer = (char *)arg;
// 查找成绩字段的索引
for (i = 0; i < f_num; i++) {
if (strcmp(f_name[i], "score") == 0) {
score_index = i;
break;
}
}
if (score_index != -1) {
strcat(response_buffer, f_name[score_index]);
strcat(response_buffer, " ");
strcat(response_buffer, f_value[score_index]);
strcat(response_buffer, "\n");
printf("%s = %s\n", f_name[score_index], f_value[score_index]);
}
return 0;
}
//查询整个表格
int do_query1(sqlite3 *db, char *request, char *response_buffer){
char *errmsg;
char **resultp;
int nrow;
int ncolumn;
int flag;
sscanf(request, "%d", &flag);
if (flag){
if (sqlite3_get_table(db, "select * from stu;", &resultp, &nrow, &ncolumn, &errmsg) != SQLITE_OK){
printf("%s\n", errmsg);
exit(0);
}
int i, j;
int index = ncolumn;
const int width = 12;
memset(response_buffer, 0, M);
for (j = 0; j < ncolumn; j++) {
char temp[width];
snprintf(temp, width, "%-12s", resultp[j]);
strcat(response_buffer, temp);
}
strcat(response_buffer, "\n");
for (i = 0; i < nrow; i++) {
for (j = 0; j < ncolumn; j++) {
char temp[width];
snprintf(temp, width, "%-12s", resultp[index++]);
strcat(response_buffer, temp);
}
strcat(response_buffer, "\n");
}
sqlite3_free_table(resultp);
}else{
memset(response_buffer, 0, M);
strcat(response_buffer, "Don't query table.\n");
}
return 0;
}
- 客户端代码 client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 128
#define M 1024
#define PORT 8888
void printInsertPrompt(char *request){
printf("Enter student information: \n");
printf("Input name: ");
fgets(request, N, stdin);
printf("Input sex: ");
fgets(request + strlen(request), N - strlen(request), stdin);
printf("Input score: ");
fgets(request + strlen(request), N - strlen(request), stdin);
}
void printDeletePrompt(char *request){
printf("Enter student id to delete: ");
fgets(request, N, stdin);
}
void printUpdatePrompt(char *request){
printf("Enter student id to update: ");
fgets(request, N, stdin);
printf("Input new score: ");
fgets(request + strlen(request), N - strlen(request), stdin);
}
void printQueryPrompt(char *request){
printf("Enter student name to query: ");
fgets(request, N, stdin);
}
void printQueryTable(char *request){
printf("Confirm to query table(1-yes 0-no): ");
fgets(request, N, stdin);
}
int main(int argc, const char *argv[])
{
int client_socket;
struct sockaddr_in server_address;
char request[N] = {};
char response[M] = {};
int choice;
int flag;
//创建客户端套接字
if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket creation failed:\n");
exit(0);
}
// 设置服务器地址和端口
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
//将IPV4地址从点分十进制转换为二进制
if (inet_aton("127.0.0.1", &server_address.sin_addr) <= 0){
perror("Invalid address/ Address not supported.\n");
close(client_socket);
exit(0);
}
//连接服务器
if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0){
perror("Connection failed.\n");
close(client_socket);
exit(0);
}
while (1){
// 提示用户选择请求类型
printf("*************************************************\n");
printf("1.insert 2.delete 3.update 4.query 5.query table\n");
printf("*************************************************\n");
printf("Input your choice: ");
scanf("%d", &choice);
getchar(); //吃掉换行符
if (write(client_socket, &choice, sizeof(int)) < 0){
perror("Write failed.\n");
exit(0);
}
switch(choice){
case 1:
printInsertPrompt(request);
flag = 0;
break;
case 2:
printDeletePrompt(request);
flag = 0;
break;
case 3:
printUpdatePrompt(request);
flag = 0;
break;
case 4:
printQueryPrompt(request);
flag = 1;
break;
case 5:
printQueryTable(request);
flag = 1;
break;
default:
printf("Invalid choice.\n");
flag = 0;
break;
}
// 向服务端发送请求
if(write(client_socket, request, strlen(request)) <= 0){
perror("Write failed.\n");
exit(0);
}
// 接受服务器响应数据
if (read(client_socket, response, sizeof(response)) < 0){
perror("Read failed.\n");
exit(0);
}
if (flag == 0){
//显示服务端的响应数据
printf("Server response: %s\n", response);
}else if (flag == 1){
printf("%s", response);
}
memset(response, 0, M);
}
//关闭套接字
close(client_socket);
return 0;
}
代码逻辑
首先,客户端执行后会在终端给出提示,选择要进行的操作进行输入,输入后客户端会将所需的请求类型发送给服务端,服务端接收到来自客户端的请求类型后进行判断,然后转到相应的处理函数中执行,执行完毕后将结果发送给客户端在客户端进行显示。
以下是交互的示例图:

