基于SQLite数据库的多进程实现TCP并发-员工管理系统

目录

  • 要求
    • 代码
      • 服务器
        • server.c
        • server.h
      • 客户端
        • client.c
        • client.h
    • 执行结果
  • 该客户端Shell的图形界面(很简单只是一个 ` 选择 `)
    • menu.sh

要求

  • 服务端支持客户端远程登录TCP
  • 支持多用户同时访问( 服务器并发 多进程 )
  • 服务端管理所有员工数据信息(用户分级管理<管理员、普通用户>)
  • 管理员可以对所有员工的数据信息进行增、删、改、查
  • 普通用户只可以查询自身信息数据 且 可以修改除薪资和职务以外的数据
  • 数据管理 sqlite3

代码

服务器

server.c

#include "./server.h"

int main(int argc, char const *argv[])
{
	//1.数据库初始化
	sqlite3 *sql_db=proc_init();
	//检验命令行参数个数
    if (3 != argc)
    {
        printf("Usage : %s  \n", argv[0]);
        exit(-1);
    }
	//3. 创建套接字-填充服务器网络信息结构体-绑定-监听
    int sockfd = socket_bind_listen(argv);

	//4. 用来保存 客户端 信息的结构体
    struct sockaddr_in client_addr;
    memset(&client_addr, 0, sizeof(client_addr));
    socklen_t client_addr_len = sizeof(client_addr);

	pid_t pid;	// 进程 
	int accept_fd;
	
	while (1)
	{
		//阻塞等待客户端连接--一旦有客户端连接就会解除阻塞
		accept_fd=accept(sockfd,(struct sockaddr *)&client_addr, &client_addr_len);
		if (-1==accept_fd)	ERRLOG("accept error");
		/* inet_ntoa 32位网络字节序二进制地址转换成点分十进制的字符串; ntohs将无符号2字节整型  网络-->主机 */
		printf("客户端[%s : %d]连接\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));


		if (0>(pid=fork())) ERRLOG("fork error");

		else if (0==pid)	// 子进程逻辑
		{
			child_do(accept_fd,sql_db);
		}
		else	//父进程逻辑
		{
			
            /*注册信号处理函数,用于防止僵尸进程;当子进程结束的时候,父进程会收到一个SIGCHLD的信号 */
			if (signal(SIGCHLD, deal_signal) == SIG_ERR) //捕获SIGCHLD
                ERRLOG("signal error");
           close(accept_fd);
		}
	}
	
	//关闭监听套接字  一般不关闭
    close(sockfd);
	return 0;
}

// 1. 数据库初始化 ---打开数据文件、建表
sqlite3 *proc_init(void)
{
	//1. 打开数据库文件
	sqlite3 *sql_db=NULL;
	int ret=sqlite3_open(FILEname,&sql_db);
	if (ret!=SQLITE_OK)
	{
		printf("打开数据库文件 失败");
        printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
        exit(-1);
	}
	//2. 建表
    /* IF NOT EXISTS  表不存在则创建  表存在则直接使用,而不是报错;引号里面sql语句后面不用加分号*/
	//管理员-密码
	char sql_rbuf[256]="CREATE TABLE IF NOT EXISTS root(id INT PRIMARY KEY, pass TEXT)";
	ret=sqlite3_exec(sql_db,sql_rbuf,NULL,NULL,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("建root表 失败");
        printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
        exit(-1);
	}
	//用户-密码-性别-年龄-住址-薪资-职务
	char sql_ubuf[256]="CREATE TABLE IF NOT EXISTS user(id INT PRIMARY KEY,pass TEXT,name TEXT,sex TEXT,age INT,pay INT,job TEXT)";
	ret=sqlite3_exec(sql_db,sql_ubuf,NULL,NULL,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("建user表 失败");
        printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
        exit(-1);
	}
	
	return sql_db;
}

//3. 创建套接字-填充服务器网络信息结构体-绑定-监听
int socket_bind_listen(const char *argv[])
{
	// 1.创建套接字      /IPV4    /TCP
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if (-1==sockfd)	ERRLOG("创建套接字 失败"); 

	// 2.填充服务器网络信息结构体
	struct sockaddr_in server_addr;
	memset(&server_addr,0,sizeof(server_addr));//清空
	server_addr.sin_family=AF_INET;			   // IPV4 
    /*端口号   atoi字符串转换成整型数; htons将无符号2字节整型  主机-->网络*/
	server_addr.sin_port=htons(atoi(argv[2]));
    /*ip地址;  inet_addr字符串转换成32位的网络字节序二进制值*/
	server_addr.sin_addr.s_addr=inet_addr(argv[1]);

	//结构体长度
	socklen_t server_addr_len=sizeof(server_addr);

	//3. 将套接字和网络信息结构体绑定
	if (-1==bind(sockfd,(struct sockaddr *)&server_addr,server_addr_len))
		ERRLOG("结构体绑定 失败"); 
	
	//4. 将套接字设置成被动监听状态
    if (-1 == listen(sockfd, 10))
        ERRLOG("listen error");
	
	return sockfd;
}

// 子进程执行
void child_do(int accept_fd, sqlite3 *sql_db)
{
	MSG msg;
	memset(&msg,0,sizeof(MSG));
    while (recv(accept_fd, &msg, sizeof(MSG), 0) > 0) // 0:阻塞;接收请求
    {
        printf("type = %d\n", msg.type); //选择
        switch (msg.type)
        {
        case R: // 1. 注册
            do_register_s(accept_fd, &msg, sql_db);
            break;
        case L: // 2. 登录
            do_login_s(accept_fd, &msg, sql_db);
            break;
        case Q: // 3. 查询
            do_query_s(accept_fd, &msg, sql_db);
            break;
        case M: // 4.修改
            do_Modify_s(accept_fd, &msg, sql_db);
			break;
		case ROOT: //ROOT 登录
            root_login_s(accept_fd, &msg, sql_db);
            break;
		case D: //删除
            root_delete_s(accept_fd, &msg, sql_db);
            break;
		case Qall: // 查询所有
            //All_query_s(accept_fd, &msg, sql_db);
			all_query_s(accept_fd,sql_db);
            break;
        }
    }
    printf("客户端 退出\n");
    exit(0);
}

// 信号处理函数  用于回收子进程资源
void deal_signal(int s)
{
	//回收子进程资源
    wait(NULL); //阻塞回收
}

// 1. 注册
void do_register_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	char sql_buf[512];
	memset(sql_buf,0,sizeof(sql_buf));
    //使用sqlite3_exec函数调用插入函数判断是否能够插入成功
    //由于用户名设置为主键,所以如果用户名已经存在就会报错
    //大写,小写容易报错
	sprintf(sql_buf,"INSERT INTO user(id,pass) VALUES(%d,'%s')", msg->id, msg->pass);
	int ret=sqlite3_exec(sql_db,sql_buf,NULL,NULL,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("注册 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
		sprintf(msg->data, "ID %d 已经存在!!!", msg->id);
	}
	else
	{
		strcpy(msg->data, "注册成功");
	}
	 //发送数据
    if (0 >= send(accept_fd,msg, sizeof(MSG), 0))
        ERRLOG("send error");
}

// 2. 登录
void do_login_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	char sql_buf[256]={0};
	//使用查询函数判断是否存在
	sprintf(sql_buf,"SELECT * FROM user WHERE id=%d AND pass='%s'", msg->id, msg->pass);
	
	char **result;	// 结果集
	int row = 0;	// 行数
	int column = 0; // 列数
					/*
					会将 查询的到的结果的 行数写到row的地址里  列数写到column的地址里
					让 result 指向 sqlite3_get_table 申请的结果集的内存空间
					*/
	//查询结果的函数                             /结果集   /行数  /列数   /错误信息
	int ret=sqlite3_get_table(sql_db,sql_buf,&result,&row,&column,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("查询 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
	}
	if (0==row)
	{
		sprintf(msg->data,"ID[%d]与密码[%s]不一致", msg->id,msg->pass);
	}
	else
    {
        strcpy(msg->data, "登录成功");
    }
	//发送数据
    if (0 >= send(accept_fd, msg, sizeof(MSG), 0))
        ERRLOG("send error");
}

// 3. 查询
void do_query_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	int id=msg->id;
	memset(msg,0,sizeof(MSG));
	msg->id=id;
	//printf("查询ID:%d\n",msg->id);
	char sql_buf[256]={0};
	
	//使用查询函数
	sprintf(sql_buf,"SELECT * FROM user WHERE id=%d",msg->id);
	char **result={0};	// 结果集
	int row = 0;	// 行数
	int column = 0; // 列数
					/*
					会将 查询的到的结果的 行数写到row的地址里  列数写到column的地址里
					让 result 指向 sqlite3_get_table 申请的结果集的内存空间
					*/
	//查询结果的函数                             /结果集   /行数  /列数   /错误信息
	int ret=sqlite3_get_table(sql_db,sql_buf,&result,&row,&column,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("查询 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
	}
	if (0==row)
	{
		sprintf(msg->data,"ID[%d]为空", msg->id);

	}
	else
    {
		if (result[column])
		{
			msg->id=atoi(result[column]);
		}
		//上输入的字符个数大于s的大小的时候,最多格式化sizeof(s)-1个字符,在s最后一个字符的
		//位置补上'\0'
		snprintf(msg->pass,sizeof(msg->pass),"%s",result[column+1]);
		snprintf(msg->name,sizeof(msg->name),"%s",result[column+2]);
		snprintf(msg->sex,sizeof(msg->sex),"%s",result[column+3]);
		if (result[column+4])
		{
			msg->age=atoi(result[column+4]);
		}
		if (result[column+5])
		{
			msg->pay=atoi(result[column+5]);
		}
		snprintf(msg->job,sizeof(msg->job),"%s",result[column+6]);
	}
	//发送数据
    if (0 >= send(accept_fd,msg, sizeof(MSG), 0))
        ERRLOG("send error");
	//释放 结果集 防止内存泄漏
	sqlite3_free_table(result);
}

// 4. 修改
void do_Modify_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	char sql_buff[512] = {0};
	// pass ,name ,sex ,age,pay,job
	sprintf(sql_buff,"UPDATE user SET pass='%s',name='%s',sex='%s',age=%d,pay=%d,job='%s' WHERE id=%d",
					msg->pass,msg->name,msg->sex,msg->age,msg->pay,msg->job,msg->id);
	//执行sql语句
	int ret = sqlite3_exec(sql_db, sql_buff, NULL, NULL, NULL);
	if (ret != SQLITE_OK)
	{
		perror("修改 失败");
		printf("返回值[%d]  错误信息[%s]\n", ret, sqlite3_errmsg(sql_db));
		exit(-1);
	}
	strcpy(msg->data, "修改成功");
	//发送数据
    if (0 >= send(accept_fd,msg, sizeof(MSG), 0))
        ERRLOG("send error");
	memset(msg,0,sizeof(MSG));
}


// 5. ROOT登录
void root_login_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	char sql_buf[256]={0};
	//使用查询函数判断是否存在
	sprintf(sql_buf,"SELECT * FROM root WHERE id=%d AND pass='%s'",123456, msg->pass);
	
	char **result;	// 结果集
	int row = 0;	// 行数
	int column = 0; // 列数
					/*
					会将 查询的到的结果的 行数写到row的地址里  列数写到column的地址里
					让 result 指向 sqlite3_get_table 申请的结果集的内存空间
					*/
	//查询结果的函数                             /结果集   /行数  /列数   /错误信息
	int ret=sqlite3_get_table(sql_db,sql_buf,&result,&row,&column,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("查询 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
	}
	if (0==row)
	{
		sprintf(msg->data,"ID[%d]与密码[%s]不一致", msg->id,msg->pass);
	}
	else
    {
        strcpy(msg->data, "登录成功");
    }
	//发送数据
    if (0 >= send(accept_fd, msg, sizeof(MSG), 0))
        ERRLOG("send error");
	memset(msg,0,sizeof(MSG));
}

//6. 删除
void root_delete_s(int accept_fd, MSG *msg, sqlite3 *sql_db)
{
	char sql_buf[256]={0};
	//填写sql语句
	sprintf(sql_buf,"DELETE FROM user WHERE id=%d",msg->id);
	//执行sql语句
	int ret = sqlite3_exec(sql_db, sql_buf, NULL, NULL, NULL);
	if (ret!=SQLITE_OK)
	{
		perror("查询 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
	}
	else
    {
        strcpy(msg->data, "删除成功");
    }
	//发送数据
    if (0 >= send(accept_fd, msg, sizeof(MSG), 0))
        ERRLOG("send error");
	memset(msg,0,sizeof(MSG));
}

// 7. 查询所有
void all_query_s(int accept_fd,sqlite3 *sql_db)
{
	MSG msg;
	int i,x;
	char sql_buf[256]={0};
	memset(&msg,0,sizeof(msg));
	//使用查询函数
	strcpy(sql_buf,"SELECT * FROM user");
	char **result;	// 结果集
	int row = 0;	// 行数
	int column = 0; // 列数
					/*
					会将 查询的到的结果的 行数写到row的地址里  列数写到column的地址里
					让 result 指向 sqlite3_get_table 申请的结果集的内存空间
					*/
	//查询结果的函数                             /结果集   /行数  /列数   /错误信息
	int ret=sqlite3_get_table(sql_db,sql_buf,&result,&row,&column,NULL);
	if (ret!=SQLITE_OK)
	{
		perror("查询 失败");
		printf("errcode[%d]  errmsg[%s]\n", ret, sqlite3_errmsg(sql_db));
	}
	if (0==row)
	{
		sprintf(msg.data,"ID[%d]为空", 0);
	}
	else
    {
		x=column;
		for (i= 0; i<row; i++)// 行数
		{
			if (result[x])
			{
				msg.id=atoi(result[x]);
			}
			//上输入的字符个数大于s的大小的时候,最多格式化sizeof(s)-1个字符,在s最后一个字符的
			//位置补上'\0'
			x++;
			snprintf(msg.pass,sizeof(msg.pass),"%s",result[x++]);
			snprintf(msg.name,sizeof(msg.name),"%s",result[x++]);
			snprintf(msg.sex,sizeof(msg.sex),"%s",result[x++]);
			if (result[x])
			{
				msg.age=atoi(result[x]);
			}
			x++;
			if (result[x])
			{
				msg.pay=atoi(result[x]);
			}
			x++;
			snprintf(msg.job,sizeof(msg.job),"%s",result[x++]);
			//发送数据
			if (0 >= send(accept_fd,&msg, sizeof(MSG), 0))
				ERRLOG("send error");
			memset(&msg,0,sizeof(msg));
			if (row==(i+1))
			{
				strcpy(msg.data,"**over**");
				//发送数据
				if (0 >= send(accept_fd,&msg, sizeof(MSG), 0))
					ERRLOG("send error");
			}				
		}	
	}
	//释放 结果集 防止内存泄漏
	sqlite3_free_table(result);
}

server.h

#ifndef __SERVER_H__
#define __SERVER_H__
#include 
#include 
#include 
#include      // memset
#include   
#include 
#include  // 信息结构体struct sockaddr_in
#include 
#include 	//inet_aton
#include 
#include 	// fork
#include 

/*数据库文件名字*/
#define FILEname "ser.db"

#define ERRLOG(errmsg)                                       \
	do                                                       \
	{                                                        \
		printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
		perror(errmsg);                                      \
	} while (0)

#define R 1 //  user register注册
#define L 2 //  user login登录
#define Q 3 //  query word查询
#define M 4 //  修改
#define ROOT 5 // root
#define D 6 // 删除
#define Qall 7 // 查询所有

#define N 32 
//信息结构体
typedef struct
{
	int type;		//选择
	int id;
	char name[N];	//用户名
	char pass[64];// password密码
	char data[256]; //emark返回数据
	char sex[32];
	int age;
	int pay;
	char job[64];
}MSG;

// 1. 数据库初始化 ---打开数据文件、建表
sqlite3 *proc_init(void);

//创建套接字-填充服务器网络信息结构体-绑定-监听
int socket_bind_listen(const char *argv[]);

// 子进程执行
void child_do(int accept_fd, sqlite3 *sql_db);

// 信号处理函数  用于回收子进程资源
void deal_signal(int s);

// 1. 注册
void do_register_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

// 2. 登录
void do_login_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

// 3. 查询
void do_query_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

// 4. 修改
void do_Modify_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

// 5. ROOT登录
void root_login_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

//6. 删除
void root_delete_s(int accept_fd, MSG *msg, sqlite3 *sql_db);

// 7. 查询所有
void all_query_s(int accept_fd,sqlite3 *sql_db);

#endif

客户端

client.c

#include "./client.h"
int errnum=0;
int root = 0,root2= 0;
int main(int argc, char const *argv[])
{
	int ret;
	//检验命令行参数个数
	if (3 != argc)
	{
		printf("Usage : %s  \n", argv[0]);
		exit(-1);
	}
	// 1. 创建套接字-填充服务器网络信息结构体-connect
	int sockfd = socket_connect(argv);

	MSG msg;
	memset(&msg, 0, sizeof(MSG));
	int n;
	while (1)
	{
		printf("************************************\n");
		printf("* 1:  登录     2: 退出             *\n");
		printf("************************************\n");
		printf("please choose : ");

		if (scanf("%d", &n) <= 0)
		{
			perror("scanf");
			exit(-1);
		}

		switch (n)
		{
		case 1: 
			//执行登录函数,执行完毕后通过返回值决定是否要跳转到下一个菜单
			if (do_login(sockfd, &msg) == 1)
				goto next;
			break;
		case 2:
			close(sockfd);
			break;
		}
	}
next:
	while (1)
	{
		if (0 == root)
		{
			printf("************************************\n");
			printf("* 1: 查询   2: 修改   3: 退出 *\n");
			printf("************************************\n");
			printf("please choose : ");

			if (scanf("%d", &n) <= 0)
			{
				perror("scanf");
				exit(-1);
			}
			switch (n)
			{
			case 1: //查询
				do_query(sockfd, &msg);
				break;
			case 2: //修改
				do_Modify(sockfd, &msg);
				break;
			case 3:
				close(sockfd);
				exit(0);
			}
		}
		if (1 == root)
		{	//增、删、改、查
			printf("********************************************************\n");
			printf("* 1: 注册(增)    2: 删除   3: 修改   4: 查询    5: 退出 *\n");
			printf("********************************************************\n");
			printf("please choose : ");

			if (scanf("%d", &n) <= 0)
			{
				perror("scanf");
				exit(-1);
			}

			switch (n)
			{
			case 1: //注册
				do_register(sockfd, &msg);
				break;
			case 2: //删除
				do_delete(sockfd, &msg);
				break;
			case 3://修改
				do_Modify(sockfd, &msg);
				break;
			case 4:
				//查询
				do_query(sockfd, &msg);
				break;
			case 5:
				root=0;
				close(sockfd);
				exit(0);
			}
		}
	}
	//关闭套接字
	close(sockfd);
	return 0;
}

// 1. 创建套接字-填充服务器网络信息结构体-connect
int socket_connect(const char *argv[])
{
	// 1.创建套接字      /IPV4    /TCP
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
		ERRLOG("创建套接字 失败");

	// 2.填充服务器网络信息结构体
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr)); //清空
	server_addr.sin_family = AF_INET;			  // IPV4
												  /*端口号   atoi字符串转换成整型数; htons将无符号2字节整型  主机-->网络*/
	server_addr.sin_port = htons(atoi(argv[2]));
	/*ip地址;  inet_addr字符串转换成32位的网络字节序二进制值*/
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);

	//结构体长度
	socklen_t server_addr_len = sizeof(server_addr);

	//与服务器建立连接
	if (-1 == connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
	{
		ERRLOG("listen error");
		exit(-1);
	}

	printf("---连接服务器成功---\n");
	return sockfd;
}

// 1.注册
void do_register(int sockfd, MSG *msg)
{
	memset(msg, 0, sizeof(MSG));
	//指定操作码
	msg->type = R;
	printf("输入ID: ");
	scanf("%d", &msg->id);
	printf("输入[%d] 密码: ", msg->id);
	scanf("%s", msg->pass);
	//发送数据
	if (0 >= send(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error");
	//接收数据并输出
	if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error");

	printf("%s\n", msg->data);
}

// 2.登录
int do_login(int sockfd, MSG *msg)
{
	char NO[32] = "n";
	char unlogin[128]={0};
	printf("是否管理员(y/n): ");
	scanf("%s", NO);
	if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
	{
		//指定操作码
		msg->type = ROOT;
		root = 1;
		printf("输入 [%d] 密码: ", msg->id);
		scanf("%s", msg->pass);
		goto root;
	}
	//指定操作码
	msg->type = L;
	root = 0;
	printf("输入ID: ");
	scanf("%d", &msg->id);
	printf("输入 [%d] 密码: ", msg->id);
	scanf("%s", msg->pass);

root:
	//发送数据
	if (0 >= send(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error ?");
	//接收数据并输出
	if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("recv error");
	printf("%s\n", msg->data);
	sprintf(unlogin,"ID[%d]与密码[%s]不一致", msg->id,msg->pass);
	if (strcmp(msg->data,unlogin)== 0)
	{
		errnum++;
		if (3==errnum)
		{
			errnum=0;
			exit(0);
		}
	}
	
	if (strcmp(msg->data, "登录成功") == 0)
	{
		if (1==root)
		{
			memset(msg,0,sizeof(MSG));
		}
		return 1;
	}
	return 0;
}

// 3. 查询
int do_query(int sockfd, MSG *msg)
{
	int id=msg->id;
	memset(msg,0,sizeof(MSG));
	msg->id=id;
	char zoor[64]={0};
	//指定操作码
	msg->type = Q;
	if (1==root&&0==root2)
	{
		printf("输入查询ID{0 all}: ");
		scanf("%d", &msg->id);
		if (0==msg->id)
		{
			goto ALLQ;
		}
	}
	//发送数据
	if (0 >= send(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error");
	//接收数据并输出
	if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("recv error");
	sprintf(zoor,"ID[%d]为空", msg->id);
	if (strcmp(msg->data, zoor) == 0)
	{
		printf("%s\n", msg->data);
	}
	else{
		printf("id     |pass   |name   |sex    |age    |pay    |job    |\n");
		printf("%d\t%s\t%s\t%s\t%d\t%d\t%s\n", msg->id, msg->pass, msg->name, msg->sex, msg->age, msg->pay, msg->job);
		errnum++;
		if (3==errnum)
		{
			errnum=0;
			printf("休息一下,您已经输错3次\n");
			exit(0);
		}
	}
	return 0;
ALLQ:
	All_query(sockfd, msg);
}

// 4. 修改
void do_Modify(int sockfd, MSG *msg)
{
	root2=1;
	if (1==root)
	{
		printf("输入修改ID: ");
		scanf("%d", &msg->id);
	}
	do_query(sockfd,msg);
	char NO[32] = "n";
	int flag=0;
	//指定操作码
	msg->type = M;
	
	printf("开始修改ID: [%d] 信息\n", msg->id);
	// msg->pass,msg->name,msg->sex,msg->age
	// pass='%s',name='%s',sex='%s',age=%d
	printf("修改密码(y/n): ");
	scanf("%s", NO);
	if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
	{
		memset(msg->pass, 0, sizeof(msg->pass));
		printf("新密码: ");
		scanf("%s", msg->pass);
		flag=1;
	}
	printf("修改名字(y/n): ");
	scanf("%s", NO);
	if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
	{
		memset(msg->name, 0, sizeof(msg->name));
		printf("新名字: ");
		scanf("%s", msg->name);
		flag=1;
	}
	printf("修改性别(y/n): ");
	scanf("%s", NO);
	if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
	{
		memset(msg->sex, 0, sizeof(msg->sex));
		printf("新性别: ");
		scanf("%s", msg->sex);
		flag=1;
	}
	printf("修改年龄(y/n): ");
	scanf("%s", NO);
	if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
	{
		printf("新年龄: ");
		scanf("%d", &msg->age);
		flag=1;
	}
	if (1==root)
	{
		printf("修改工资(y/n): ");
		scanf("%s", NO);
		if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
		{
			printf("新工资: ");
			scanf("%d", &msg->pay);
			flag=1;
		}
		printf("修改职位(y/n): ");
		scanf("%s", NO);
		if (strcmp(NO, "y") == 0 || strcmp(NO, "Y") == 0)
		{
			printf("新职位: ");
			scanf("%s", msg->job);
			flag=1;
		}
	}
	
	if (0==flag)
	{
		printf("无更新\n");
	}
	else
	{
		//发送数据
		if (0 >= send(sockfd, msg, sizeof(MSG), 0))
			ERRLOG("send error");
		//接收数据并输出
		if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
			ERRLOG("recv error");

		if (strcmp(msg->data, "修改成功") == 0)
		{
			printf("%s\n", msg->data);
		}
	}
	root2=0;
}

//删除
void do_delete(int sockfd, MSG *msg)
{
	
	printf("输入删除ID: ");
	scanf("%d", &msg->id);
	//指定操作码
	msg->type = D;
	//发送数据
	if (0 >= send(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error");
	//接收数据并输出
	if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("recv error");
	printf("%s\n", msg->data);
}

// 7. 查询所有
void All_query(int sockfd, MSG *msg)
{
	memset(msg, 0, sizeof(MSG));
	int flag=0;
	char zoor[64]={0};
	sprintf(zoor,"ID[%d]为空", 0);
	//指定操作码
	msg->type = Qall;
	//发送数据
	if (0 >= send(sockfd, msg, sizeof(MSG), 0))
		ERRLOG("send error");
	while (1)
	{
		//接收数据并输出
		if (0 >= recv(sockfd, msg, sizeof(MSG), 0))
			ERRLOG("recv error");
		if (strcmp(msg->data, zoor) == 0)
		{
			printf("%s\n", msg->data);
		}
		if (strcmp(msg->data,"**over**") == 0)
		{
			printf("输出成功\n");
			break;
		}
		else
		{
			if (0==flag)
			{
				printf("id     |pass   |name   |sex    |age    |pay    |job    |\n");
				flag=1;
			}
			printf("%d\t%s\t%s\t%s\t%d\t%d\t%s\n", msg->id, msg->pass, msg->name, msg->sex, msg->age, msg->pay, msg->job);
			memset(msg, 0, sizeof(MSG));
		}
	}
	flag=0;
}

client.h

#ifndef __SERVER_H__
#define __SERVER_H__
#include 
#include 
#include 
#include      // memset
#include   
#include 
#include  // 信息结构体struct sockaddr_in
#include 
#include 	//inet_aton
#include 
#include 	
#define ERRLOG(errmsg)                                       \
	do                                                       \
	{                                                        \
		printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
		perror(errmsg);                                      \
	} while (0)

#define R 1 //  user register注册
#define L 2 //  user login登录
#define Q 3 //  query word查询
#define M 4 //  修改
#define ROOT 5 // root
#define D 6 // 删除
#define Qall 7 // 查询所有

#define N 32 
//信息结构体
typedef struct
{
	int type;		//选择
	int id;
	char name[N];	//用户名
	char pass[64];// password密码
	char data[256]; //emark返回数据
	char sex[32];
	int age;
	int pay;
	char job[64];
}MSG;

//1. 创建套接字-填充服务器网络信息结构体-connect
int socket_connect(const char *argv[]);

//1.注册
void do_register(int sockfd,MSG *msg);

//2.登录
int do_login(int sockfd, MSG *msg);

// 3. 查询
int do_query(int sockfd, MSG *msg);

//4. 修改
void do_Modify(int sockfd, MSG *msg);

//删除
void do_delete(int sockfd, MSG *msg);

// 7. 查询所有
void All_query(int sockfd, MSG *msg);

#endif

执行结果

基于SQLite数据库的多进程实现TCP并发-员工管理系统_第1张图片
基于SQLite数据库的多进程实现TCP并发-员工管理系统_第2张图片

该客户端Shell的图形界面(很简单只是一个 选择

  • sudo apt-get install dialog

menu.sh

#!/bin/bash

# simple script menu

function Performing {
    clear
    
    cd /home/linux/xxxxxxxxx目录(pwd)查看xxxxxxxxx/03epoll员工/客户端
    
    dialog --title "Questionnaire" --inputbox "请输入端口号 " 9 30 2>_1.txt
    port=$(cat _1.txt)
    ./cli 127.0.0.1 $port
}

function menu {
    clear
	# 重定向标准错误输出流(2)到“_1.txt”。
    dialog --menu "    员工管理系统  " 15 30 2 1 "进入" 0 "退出"  2>_1.txt
    if [ $? -ne 0 ]; then  	# yes的返回码为0,“$?”为上一个命令的退出状态。
        dialog --clear  	# “-clear”作用为清屏。
        clear
        exit 0
    fi
    option=$(cat _1.txt)

}

while [ 1 ]; do

    menu

    case $option in

    0)

        break
        ;;

    1)

        Performing
        ;;

    *)
        clear
        echo "对不起,错误的选择"
        ;;

    esac

done

clear
  1. bash menu.sh
    基于SQLite数据库的多进程实现TCP并发-员工管理系统_第3张图片
  2. 输入端口号在这里插入图片描述
  3. 本质就是 ./cli 127.0.0.1 8888基于SQLite数据库的多进程实现TCP并发-员工管理系统_第4张图片

你可能感兴趣的:(SQLite,Linux&C,数据库,sqlite,tcp/ip)