IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 基于SQLite数据库的IO多路复用(epoll)实现TCP并发-员工管理系统 -> 正文阅读

[网络协议]基于SQLite数据库的IO多路复用(epoll)实现TCP并发-员工管理系统

要求

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

一个帐号在同一时间只能一个人登录( 如QQ另一设备登录前一设备退出 )

基于SQLite数据库的IO多路复用(epoll)实现TCP并发-员工管理系统{一个帐号只能一个人登录}

代码

服务器

server.c

#include "./server.h"

int main(int argc, char const *argv[])
{
	//1.数据库初始化
	sqlite3 *sql_db=proc_init();
	//检验命令行参数个数
    if (3 != argc)
    {
        printf("Usage : %s <IP> <PORT>\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);

	//epoll函数实现io多路复用
	//创建一个epoll
	int epfd=epoll_create(10);//内部数字没意义,填大于零数字即可
	if (-1==epfd)	ERRLOG("epoll_create error");

	struct epoll_event event;		//事件结构体
	event.events=EPOLLIN;//监控读;EPOLLIN读;EPOLLOUT写
	event.data.fd=sockfd;//监控的套接字;将sockfd套接字加入监控集合
	//                    -添加
	if (0!=epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event))
		ERRLOG("epoll_ctl ADD error");
	
	struct epoll_event revents[256]={0};//监控的最大个数,创建监控表,用于epoll_wait函数使用
	int ret_epoll=0;//承接epoll_wait函数返回值,返回准备好的文件描述符个数
	int i=0;//用于循环筛选准备好的文件描述符
	int accept_fd;
	int ret;
	
	while (1)
	{
		ret_epoll= epoll_wait(epfd,revents,sizeof(revents),-1);// -1:不关心超时
		if(-1==ret_epoll)
			ERRLOG("epoll wait error");
		else
		{
			//for循环遍历监控表
			for ( i = 0; i <ret_epoll; i++)
			{
				if (revents[i].data.fd==sockfd)
				{
					//阻塞等待客户端连接--一旦有客户端连接就会解除阻塞
					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));
					event.events=EPOLLIN;//监控读;EPOLLIN读;EPOLLOUT写
					event.data.fd=accept_fd;//监控的套接字
					//                    -添加
					if (0!=epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&event))
						ERRLOG("epoll_ctl ADD error");
				}
				else
				{
					//说明有客户端发来消息了
                    accept_fd = revents[i].data.fd;
                    //接收客户端发来的数据
					ret=child_do(accept_fd,sql_db);
					if (88==ret)
					{
						//                    - 删除
						if (0!=epoll_ctl(epfd,EPOLL_CTL_DEL,accept_fd,&event))
							ERRLOG("epoll_ctl ADD 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;
}

// 执行
int child_do(int accept_fd, sqlite3 *sql_db)
{
	MSG msg;
	memset(&msg,0,sizeof(MSG));
	if (0==recv(accept_fd, &msg, sizeof(MSG), 0))
	{
		printf("客户端 退出\n");
		return 88;// 返回值用于退出判断
	}
	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,sql_db);
		break;
	}
	return 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 <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>     // memset
#include <sys/types.h>  
#include <sys/socket.h>
#include <netinet/in.h> // 信息结构体struct sockaddr_in
#include <signal.h>
#include <netinet/in.h>	//inet_aton
#include <arpa/inet.h>
#include <unistd.h>	// fork
#include <sys/wait.h>
#include <sys/epoll.h>

/*数据库文件名字*/
#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;

//登录成功信息结构体
typedef struct
{
	int accept_fd;		//选择
	int id;
	int ord_accept_fd;
}LOG_SUCC;

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

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

// 子进程执行
int 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 <IP> <PORT>\n", argv[0]);
		exit(-1);
	}
	// 1. 创建套接字-填充服务器网络信息结构体-connect
	int sockfd = socket_connect(argv);

	MSG msg;
	memset(&msg, 0, sizeof(MSG));
	int n;
	char nn;
	fd_set readfds;    //母本
	FD_ZERO(&readfds); //清空
	fd_set readfds_temp;    //用来备份原集合的
	FD_ZERO(&readfds_temp); //清空
	int max_fd = 0;//记录表中最大的文件描述符
	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);
			exit(0);
		}
	}
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);
			}
			switch (nn)
			{
			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;
			printf("休息一下,您已经输错3次\n");
			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);
		errnum=0;
		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);
		if (0==root2)
		{
			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 <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>     // memset
#include <sys/types.h>  
#include <sys/socket.h>
#include <netinet/in.h> // 信息结构体struct sockaddr_in
#include <signal.h>
#include <netinet/in.h>	//inet_aton
#include <arpa/inet.h>
#include <unistd.h>	
#include <sys/select.h>

#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

执行结果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

该客户端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
    在这里插入图片描述
  2. 输入端口号在这里插入图片描述
  3. 本质就是 ./cli 127.0.0.1 8888在这里插入图片描述
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 11:14:36  更:2022-08-06 11:15:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 21:44:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计