项目要求
1)服务器负责管理所有员工表单(以数据库形式),其他客户端可通过网络连接服务器来查询员工表单。
2)需要账号密码登陆,其中需要区分管理员账号还是普通用户账号。
3)管理员账号可以查看、修改、添加、删除员工信息,同时具有查询历史记录功能,管理员要负责管理所有的普通用户。
4)普通用户只能查询修改与本人有关的相关信息,其他员工信息不得查看修改。
5)服务器能同时相应多台客户端的请求功能。并发
项目涉及的知识点
1.TCP通信的服务器、客户端编程流程
1.服务器:
1)创建套接字
2)绑定ip和端口号
3)监听
4)等待客户端连接
2.客户端:
1)创建套接字
2)连接服务器
2.服务器并发
1)多线程
2)多进程
3)IO多路复用:select
3.sqlite3数据库常用语句以及编程接口
编译数据库接口相关c文件需要链接库 -lsqlite3
项目流程图
服务器
客户端
通信结构体的构建
/*员工基本信息*/
typedef struct staff_info{
int no; //员工编号
int usertype; //ADMIN 0 USER 1
char name[NAMELEN]; //姓名
char passwd[8]; //密码
int age; // 年龄
char phone[NAMELEN];//电话
char addr[DATALEN]; // 地址
char work[DATALEN]; //职位
char date[DATALEN]; //入职年月
int level; // 等级
double salary ; // 工资
}staff_info_t;
/*定义双方通信的结构体信息*/
typedef struct {
int msgtype; //请求的消息类型
int usertype; //ADMIN 0 USER 1
char username[NAMELEN]; //姓名
char passwd[8]; //登陆密码
char recvmsg[DATALEN]; //通信的消息
int flags; //标志位
void *released;
staff_info_t info; //员工信息
}MSG;
代码
头文件
#ifndef _COMMON_H_
#define _COMMON_H_
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sqlite3.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <pthread.h>
#define STAFF_DATABASE "staff_manage_system.db"
#define USER_LOGIN 0x00000000 // login 登陆 0x00000001
#define USER_MODIFY 0x00000001 // user-modification 修改
#define USER_QUERY 0x00000002 // user-query 查询
#define ADMIN_LOGIN 0x10000000 // login 登陆 0x00000001
#define ADMIN_MODIFY 0x10000001 // admin_modification 修改
#define ADMIN_ADDUSER 0x10000002 // admin_adduser 添加
#define ADMIN_DELUSER 0x10000004 // admin_deluser 删除
#define ADMIN_QUERY 0x10000008 //hitory_query 查找
#define ADMIN_HISTORY 0x10000010 //hitory_history 历史
#define ADMIN_LIST 0x10000011 //列表
#define QUIT 0x11111111
#define ADMIN 0 //管理员
#define USER 1 //用户
#define PASSLEN 8
#define NAMELEN 16
#define DATALEN 128
/*员工基本信息*/
typedef struct staff_info
{
int no; //员工编号
int usertype; //ADMIN 1 USER 2
char name[NAMELEN]; //姓名
char passwd[PASSLEN]; //密码
int age; // 年龄
char phone[NAMELEN]; //电话
char addr[DATALEN]; // 地址
char work[DATALEN]; //职位
char date[DATALEN]; //入职年月
int level; // 等级
int salary; // 工资
}staff_info_t;
/*定义双方通信的结构体信息*/
typedef struct
{
int msgtype; //请求的消息类型
int usertype; //ADMIN 1 USER 2
char username[NAMELEN]; //姓名
char passwd[PASSLEN]; //登陆密码
char recvmsg[DATALEN]; //通信的消息
int flags; //标志位
staff_info_t info; //员工信息
}MSG;
#define S_IP "10.0.12.9"
#define IP "192.168.250.100"
#define PORT "8888"
char user_name[NAMELEN]; //姓名
char user_passwd[PASSLEN]; //登陆密码
char history[DATALEN+100];
#endif
服务端
...
客户端
#include "common.h"
int do_login(int socketfd);
/**************************************
*函数名:do_query
*参 数:消息结构体
*功 能:管理员查询
****************************************/
void do_admin_query(int sockfd, MSG *msg)
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = ADMIN_QUERY;
char name[NAMELEN] = "";
printf("Please enter find no:");
scanf("%s", name);
while (getchar() != '\n')
;
strcpy(msg->recvmsg, name);
//发送查询请求
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
//接受服务器响应
while (1)
{
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "end", 3))
{
printf("find success\n");
return;
}
if (!strncmp(msg->recvmsg, "no", 2))
{
printf("find error\n");
return;
}
printf("----------------------------------\n");
printf("staffno :%d\nusertype:%d\nname :%s\npasswd :%s\nage :%d\nphone :%s\naddr :%s\nwork :%s\ndate :%s\nlevel :%d\nsalary :%d\n", msg->info.no, msg->info.usertype, msg->info.name, msg->info.passwd, msg->info.age, msg->info.phone, msg->info.addr, msg->info.work, msg->info.date, msg->info.level, msg->info.salary);
printf("----------------------------------\n");
}
}
/**************************************
*函数名:admin_modification
*参 数:消息结构体
*功 能:管理员修改
****************************************/
void do_admin_modification(int sockfd, MSG *msg) //管理员修改
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = ADMIN_MODIFY;
int opt = 0;
int tem;
char str1[NAMELEN] = "";
char str2[DATALEN] = "";
char str3[PASSLEN] = "";
printf("please enter user name:");
scanf("%s",msg->username);
printf("1:no\n2:usertype\n3:name\n4:passwd\n5:age\n6:phone\n7:addr\n8:work\n9:date\n10:level\n11:salary\nplease enter change option:");
scanf("%d", &opt);
while(getchar()!='\n');
switch (opt)
{
case 1:
msg->flags = 1;
printf("please enter no:");
scanf("%d", &tem);
msg->info.no = tem;
break;
case 2:
msg->flags = 2;
printf("please enter usertype:");
scanf("%d", &tem);
msg->info.usertype = tem;
break;
case 3:
msg->flags = 3;
printf("please enter name:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.name, str1);
break;
case 4:
msg->flags = 4;
printf("please enter passwd:");
fgets(str3,PASSLEN,stdin);
str3[strlen(str3)-1]='\0';
strcpy(msg->info.passwd, str1);
break;
case 5:
msg->flags = 5;
printf("please enter age:");
scanf("%d", &tem);
msg->info.age = tem;
break;
case 6:
msg->flags = 6;
printf("please enter phone:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.phone, str1);
break;
case 7:
msg->flags = 7;
printf("please enter addr:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.addr, str2);
break;
case 8:
msg->flags = 8;
printf("please enter work:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.work, str2);
break;
case 9:
msg->flags = 9;
printf("please enter date:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.date, str2);
break;
case 10:
msg->flags = 10;
printf("please enter level:");
scanf("%d", &tem);
msg->info.level = tem;
break;
case 11:
msg->flags = 11;
printf("please enter salary:");
scanf("%d", &tem);
msg->info.salary = tem;
break;
default:
printf("enter error\n");
return;
}
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
system("clear");
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "ok", 2))
{
printf("user change success\n");
return;
}
else if(!strncmp(msg->recvmsg, "no", 2))
{
printf("user change error\n");
return;
}
}
/**************************************
*函数名:admin_adduser
*参 数:消息结构体
*功 能:管理员创建用户
****************************************/
void do_admin_adduser(int sockfd, MSG *msg) //管理员添加用户
{
system("clear");
int tem;
char str1[NAMELEN] = "";
char str2[DATALEN] = "";
char str3[PASSLEN] = "";
memset(msg, 0, sizeof(MSG));
msg->msgtype = ADMIN_ADDUSER;
printf("please enter no:");
scanf("%d", &tem);
msg->info.no = tem;
printf("please enter usertype:");
scanf("%d", &tem);
msg->info.usertype = tem;
while (getchar() != '\n');
printf("please enter name:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.name, str1);
printf("please enter passwd:");
fgets(str3,PASSLEN,stdin);
str3[strlen(str3)-1]='\0';
strcpy(msg->info.passwd, str3);
printf("please enter age:");
scanf("%d", &tem);
msg->info.age = tem;
while (getchar() != '\n');
printf("please enter phone:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.phone, str1);
printf("please enter addr:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.addr, str2);
printf("please enter work:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.work, str2);
printf("please enter date:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.date, str2);
printf("please enter level:");
scanf("%d", &tem);
msg->info.level = tem;
printf("please enter salary:");
scanf("%d", &tem);
msg->info.salary = tem;
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "ok", 2))
{
printf("user add success\n");
return;
}
else if (!strncmp(msg->recvmsg, "no", 2))
{
printf("user add error\n");
return;
}
printf("user add error\n");
}
/**************************************
*函数名:admin_deluser
*参 数:消息结构体
*功 能:管理员删除用户
****************************************/
void do_admin_deluser(int sockfd, MSG *msg) //管理员删除用户
{
system("clear");
msg->msgtype = ADMIN_DELUSER;
int no = 0;
printf("pelase enter delete user NO:");
scanf("%d", &no);
msg->info.no = no;
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "ok", 2))
{
printf("user delete success\n");
return;
}
else if (!strncmp(msg->recvmsg, "no", 2))
{
printf("user delete error\n");
return;
}
}
/**************************************
*函数名:do_history
*参 数:消息结构体
*功 能:管理员查看历史记录
****************************************/
void do_admin_history(int sockfd, MSG *msg)
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = ADMIN_HISTORY;
putchar(10);
printf(" %s | %s | %s \n","data","time","log");
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
while (1)
{
recv(sockfd, msg, sizeof(MSG), 0);
if (msg->flags == 1)
{
printf("find success\n");
break;
}
if (msg->flags == 2)
{
printf("find error\n");
break;
}
printf("%s",msg->recvmsg);
send(sockfd, msg, sizeof(MSG), 0);
}
}
void do_admin_list(int sockfd, MSG *msg)
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = ADMIN_LIST;
char name[NAMELEN] = "";
//发送查询请求
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
//接受服务器响应
while (1)
{
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "endd", 4))
{
printf("find success\n");
return;
}
if (!strncmp(msg->recvmsg, "no", 2))
{
printf("find error\n");
return;
}
//printf("%d\t%d\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%d\n", msg->info.no, msg->info.usertype, msg->info.name, msg->info.passwd, msg->info.age, msg->info.phone, msg->info.addr, msg->info.work, msg->info.date, msg->info.level, msg->info.salary);
printf("----------------------------------\n");
printf("staffno :%d\nusertype:%d\nname :%s\npasswd :%s\nage :%d\nphone :%s\naddr :%s\nwork :%s\ndate :%s\nlevel :%d\nsalary :%d\n", msg->info.no, msg->info.usertype, msg->info.name, msg->info.passwd, msg->info.age, msg->info.phone, msg->info.addr, msg->info.work, msg->info.date, msg->info.level, msg->info.salary);
printf("----------------------------------\n");
send(sockfd, msg, sizeof(MSG), 0);
}
}
/**************************************
*函数名:admin_menu
*参 数:套接字、消息结构体
*功 能:管理员菜单
****************************************/
void admin_menu(int sockfd, MSG *msg)
{
int op = -1;
printf(" ************ 1 查看 ************\n");
printf(" ************ 2 修改 ************\n");
printf(" ************ 3 添加 ************\n");
printf(" ************ 4 删除 ************\n");
printf(" ************ 5 列表 ************\n");
printf(" ************ 6 清屏 ************\n");
printf(" ************ 7 历史记录 ************\n");
printf(" ************ 0 退回上级 ************\n");
printf("请输入选项:");
scanf("%d", &op);
while(getchar()!='\n');
if(op < 0 || op > 7)
{
system("clear");
printf("输入错误 请重新输入\n");
admin_menu(sockfd,msg);
}
switch (op)
{
case 0:
system("clear");
do_login(sockfd);
break;
case 1:
do_admin_query(sockfd, msg);
break;
case 2:
do_admin_modification(sockfd, msg);
break;
case 3:
do_admin_adduser(sockfd, msg);
break;
case 4:
do_admin_deluser(sockfd, msg);
break;
case 5:
do_admin_list(sockfd, msg);
break;
case 6:
system("clear");
break;
case 7:
do_admin_history(sockfd,msg);
break;
default:
printf("enter error\n");
return;
}
}
/**************************************
*函数名:do_query
*参 数:消息结构体
*功 能:用户查找
****************************************/
void do_user_query(int sockfd, MSG *msg)
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = USER_QUERY;
strcpy(msg->username, user_name);
strcpy(msg->passwd,user_passwd);
//发送查询请求
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
//接受服务器响应
while(1)
{
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "end", 3))
{
printf("find success\n");
return;
}
if (!strncmp(msg->recvmsg, "no", 2))
{
printf("find error\n");
return;
}
printf("----------------------------------\n");
printf("staffno :%d\nusertype:%d\nname :%s\npasswd :%s\nage :%d\nphone :%s\naddr :%s\nwork :%s\ndate :%s\nlevel :%d\nsalary :%d\n", msg->info.no, msg->info.usertype, msg->info.name, msg->info.passwd, msg->info.age, msg->info.phone, msg->info.addr, msg->info.work, msg->info.date, msg->info.level, msg->info.salary);
printf("----------------------------------\n");
}
}
/**************************************
*函数名:do_modification
*参 数:消息结构体
*功 能:用户修改
****************************************/
void do_user_modification(int sockfd, MSG *msg)
{
system("clear");
memset(msg, 0, sizeof(MSG));
msg->msgtype = USER_MODIFY;
strcpy(msg->username, user_name);
strcpy(msg->passwd,user_passwd);
int opt = 0;
int tem;
char str1[NAMELEN] = "";
char str2[DATALEN] = "";
char str3[PASSLEN] = "";
printf("1:no\n2:usertype(Please contact the administrator)\n3:name\n4:passwd\n5:age\n6:phone\n7:addr\n8:work\n9:date\n10:level\n11:salary\nplease enter change option:");
scanf("%d", &opt);
while(getchar()!='\n');
switch (opt)
{
case 1:
msg->flags = 1;
printf("please enter no:");
scanf("%d", &tem);
msg->info.no = tem;
break;
case 2:
msg->flags = 2;
printf("please enter usertype:");
scanf("%d", &tem);
msg->info.usertype = tem;
break;
case 3:
msg->flags = 3;
printf("please enter name:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.name, str1);
break;
case 4:
msg->flags = 4;
printf("please enter passwd:");
fgets(str3,PASSLEN,stdin);
str3[strlen(str3)-1]='\0';
strcpy(msg->info.passwd, str3);
break;
case 5:
msg->flags = 5;
printf("please enter age:");
scanf("%d", &tem);
msg->info.age = tem;
break;
case 6:
msg->flags = 6;
printf("please enter phone:");
fgets(str1,NAMELEN,stdin);
str1[strlen(str1)-1]='\0';
strcpy(msg->info.phone, str1);
break;
case 7:
msg->flags = 7;
printf("please enter addr:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.addr, str2);
break;
case 8:
msg->flags = 8;
printf("please enter work:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.work, str2);
break;
case 9:
msg->flags = 9;
printf("please enter date:");
fgets(str2,DATALEN,stdin);
str2[strlen(str2)-1]='\0';
strcpy(msg->info.date, str2);
break;
case 10:
msg->flags = 10;
printf("please enter level:");
scanf("%d", &tem);
msg->info.level = tem;
break;
case 11:
msg->flags = 11;
printf("please enter salary:");
scanf("%d", &tem);
msg->info.salary = tem;
break;
default:
printf("enter error\n");
return;
}
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return;
}
system("clear");
recv(sockfd, msg, sizeof(MSG), 0);
if (!strncmp(msg->recvmsg, "ok", 2))
{
printf("user change success\n");
return;
}
else if(!strncmp(msg->recvmsg, "no", 2))
{
printf("user change error\n");
return;
}
}
/**************************************
*函数名:user_menu
*参 数:消息结构体
*功 能:用户菜单
****************************************/
void user_menu(int sockfd, MSG *msg)
{
int op = -1;
printf(" ************ 1 查看 ************\n");
printf(" ************ 2 修改 ************\n");
printf(" ************ 3 清屏 ************\n");
printf(" ************ 0 退回上级 *********\n");
printf("请输入选项:");
scanf("%d", &op);
while(getchar()!='\n');
if(op < 0 || op > 3)
{
system("clear");
printf("输入错误 请重新输入\n");
user_menu(sockfd,msg);
}
switch (op)
{
case 0:
system("clear");
do_login(sockfd);
break;
case 1:
do_user_query(sockfd, msg);
break;
case 2:
do_user_modification(sockfd, msg);
break;
case 3:
system("clear");
break;
default:
printf("enter error\n");
break;
}
}
int admin_or_user_login(int sockfd, MSG *msg)
{
//输入用户名和密码
memset(msg->username, 0, NAMELEN);
printf("请输入用户名:");
scanf("%s", msg->username);
getchar();
memset(msg->passwd, 0, DATALEN);
printf("请输入密码: ");
scanf("%s", msg->passwd);
getchar();
strcpy(user_name,msg->username);
strcpy(user_passwd,msg->passwd);
//发送登陆请求
if (send(sockfd, msg, sizeof(MSG), 0) < 0)
{
printf("Sending the account to the server error\n");
return -1;
}
//接受服务器响应
recv(sockfd, msg, sizeof(MSG), 0);
//判断是否登陆成功
if (strncmp(msg->recvmsg, "OK", 2) == 0)
{
if (msg->usertype == ADMIN)
{
system("clear");
printf("亲爱的管理员,欢迎您登陆员工管理系统!\n");
while (1)
{
admin_menu(sockfd, msg);
}
}
else if (msg->usertype == USER)
{
system("clear");
printf("亲爱的用户,欢迎您登陆员工管理系统!\n");
while (1)
{
user_menu(sockfd, msg);
}
}
}
else
{
printf("登陆失败!%s\n", msg->recvmsg);
admin_or_user_login(sockfd,msg);
}
return 0;
}
/************************************************
*函数名:do_login
*参 数:套接字、消息结构体
*返回值:是否登陆成功
*功 能:登陆
*************************************************/
int do_login(int socketfd)
{
int n;
MSG msg;
while (1)
{
printf("**********************************\n");
printf("******** 1: 管理员模式 ********\n");
printf("******** 2:普通用户模式 ********\n");
printf("******** 0: 断开客户端 ********\n");
printf("**********************************\n");
printf("请输入您的选择(数字)>> ");
scanf("%d", &n);
while(getchar()!='\n');
if(n < 0 || n > 2)
{
system("clear");
printf("输入错误 请重新输入\n");
do_login(socketfd);
}
switch (n)
{
case 1:
//管理员模式登录
msg.msgtype = ADMIN_LOGIN; // 1
msg.usertype = ADMIN; // 0
break;
case 2:
//普通用户登录
msg.msgtype = USER_LOGIN;
msg.usertype = USER;
break;
case 0:
//退出
msg.msgtype = QUIT;
if (send(socketfd, &msg, sizeof(MSG), 0) < 0)
{
perror("do_login send");
return -1;
}
close(socketfd);
exit(0);
default:
printf("您的输入有误,请重新输入\n");
}
admin_or_user_login(socketfd, &msg);
}
}
int main(int argc, const char *argv[])
{
// socket->填充->绑定->监听->等待连接->数据交互->关闭
system("clear");
int socketfd;
//创建网络通信的套接字 流式套接字
if (-1 == (socketfd = socket(AF_INET, SOCK_STREAM, 0)))
{
printf("socket error\n");
return -1;
}
printf("socket success\n");
//填充网络结构体
//填充服务器网路信息结构体
struct sockaddr_in sin;
//填充为IPV4地址
sin.sin_family = AF_INET;
//填充服务器IP
sin.sin_addr.s_addr = inet_addr(IP);
//填充服务器端口号
sin.sin_port = htons(atoi(PORT));
//连接服务器
if (-1 == connect(socketfd, (struct sockaddr *)&sin, sizeof(sin)))
{
printf("connect error\n");
return -1;
}
printf("connect suceess\n");
//登陆
do_login(socketfd);
//关闭套接字
close(socketfd);
return 0;
}
测试现象
Ubuntu 18.04 PZB - VMware