此项目为linux 下多进程编程银行小系统(消息队列版本)改进版
通过socket套接字以及tcp协议是客户端和服务端可以通过本地网络交互数据
之前的消息队列版本:传送门
网上银行版本:码云传送门;github传送门
总体思路:
依旧分客户端(client)和服务端(server)两个端口,将需要传递数据的账户结构体放在bank.h中,tools.c中放一些常用的工具函数。
客户端发送数据,包括操作数据类型和实际的账户数据,服务端则根据接收的不同的数据类型,执行不同的操作等,再将结果存入buf字符串发送给客户端接收,客户端接收返回结果并显示。
项目要求
主要分为两人大模块:
客户端
1、进入时的功能开户、销户、登录、解锁
开户:输入姓名、身份证号、设置密码,如果开户成功,则服务器上保存一个账号信号(一个账号存一个文件,文件名建议是账号)。
销户:输入帐号、密码,服务器询问是否确认销户,如果确认则服务器删除帐号文件,并记录帐号。
登录:输入账号、密码,三次错误账号锁定。
解锁:输入账号、身份证号解锁。
2、登录成功:存钱、取钱、转账、查询、修改密码
存钱:输入存钱金额
取钱:输入取钱金额
转账:目标帐号和要转的金额
查询:不需要输入数据
修改密码:原密码和新密码
服务器
如果识别功能:根据消息的类型识别客户端请求的功能。
开启服务各项功能的子进程
各进程按照消息类型接收消息
头文件
#define key 123456
帐号结构体
{
帐号
身份证号
密码
金额
}
消息结构体
{
消息类型
帐号结构体
}
客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "tools.h"
#include "bank.h"
#define BUF_SIZE (4096)
int cli_fd;
Acc acc = {1,"1","1","1",1,1};
void show_acc(Acc* acc)
{
printf("%d %s %s %s %f %d\n",acc->type,acc->bank,acc->card,acc->pass,acc->balance,acc->islock);
}
void acc_to_str(Acc* acc,char* buf)
{
sprintf(buf,"%d %s %s %s %f %d",acc->type,acc->bank,acc->card,acc->pass,acc->balance,acc->islock);
}
// 主菜单
void main_menu(void)
{
puts("******欢迎使用指针银行******");
puts("1、开户 2、登陆");
puts("3、销户 4、解锁");
puts("0、退出");
}
// 子菜单
void sub_menu(void)
{
puts("******恭喜您登陆成功******");
puts("1、存款 2、取款");
puts("3、查询 4、转账");
puts("5、改密 6、退出");
}
// 存款
void save(void)
{
acc.type = M_SAVE;
printf("请输入存款金额:");
scanf("%f",&acc.balance);
}
// 取款
void take(void)
{
acc.type = M_TAKE;
printf("请输入取款金额:");
scanf("%f",&acc.balance);
}
// 查询
void query(void)
{
acc.type = M_QUERY;
}
// 转账
void transfer(void)
{
acc.type = M_TRANSFER;
printf("请输入目标银行卡号:");
get_str(acc.card,20);
printf("请输入转账金额:");
scanf("%f",&acc.balance);
}
// 改密
void repass(void)
{
char pass1[20] = {} , pass2[20] = {};
printf("请输入新密码:");
get_pass(pass1,20,true);
printf("请再次输入新密码:");
get_pass(pass2,20,true);
if(strcmp(pass1,pass2))
{
puts("两次输入的密码不相同,修改失败!");
}
else
{
acc.type = M_REPASS;
strcpy(acc.pass,pass1);
}
}
// 开户
void open_acc(void)
{
acc.type = M_OPEN;
printf("请输入身份证号:");
get_str(acc.card,20);
printf("请输入开户金额:");
scanf("%f",&acc.balance);
}
// 登陆
void login(void)
{
acc.type = M_LOGIN;
printf("请输入银行卡号:");
get_str(acc.bank,20);
printf("请输入密码:");
//get_str(acc.pass,20);
get_pass(acc.pass,20,true);
// 向服务端发送消息
char buf[BUF_SIZE];
sprintf(buf,"%d %s %s %s %f %d",acc.type,acc.bank,acc.card,acc.pass,acc.balance,acc.islock);
int send_size = write(cli_fd,buf,strlen(buf)+1);
// 从服务端接收消息
int recv_size = read(cli_fd,buf,sizeof(buf));
// 显服务端执行的结果
puts(buf);
anykey_continue();
if('Y' != buf[0])
{
return;
}
for(;;)
{
sub_menu();
switch(get_cmd('1','6')-'0'+4)
{
case M_SAVE: save(); break; // 存款
case M_TAKE: take(); break; // 取款
case M_QUERY: query(); break; // 查询
case M_TRANSFER:transfer(); break; // 转账
case M_REPASS: repass(); break; // 改密
default: return ;break;// 退出
}
// 向服务端发送消息
char msg[BUF_SIZE];
sprintf(msg,"%d %s %s %s %f %d",acc.type,acc.bank,acc.card,acc.pass,acc.balance,acc.islock);
write(cli_fd,msg,strlen(msg)+1);
// 从服务端接收消息
read(cli_fd,msg,sizeof(msg));
// 显服务端执行的结果
puts(msg);
anykey_continue();
}
}
// 销户
void destory(void)
{
acc.type = M_DESTORY;
printf("请输入银行卡号:");
get_str(acc.bank,20);
printf("请输入身份证号:");
get_str(acc.card,20);
printf("请输入密码:");
get_pass(acc.pass,20,true);
}
// 解锁
void unlock(void)
{
acc.type = M_UNLOCK;
printf("请输入银行卡号:");
get_str(acc.bank,20);
printf("请输入身份证号:");
get_str(acc.card,20);
printf("请输入密码:");
get_pass(acc.pass,20,true);
}
void exit_bank(void)
{
char buf[BUF_SIZE];
sprintf(buf,"quit");
write(cli_fd,buf,strlen(buf)+1);
close(cli_fd);
exit(0);
}
int main()
{
//创建socket对象
cli_fd = socket(AF_INET,SOCK_STREAM,0);
if(0 > cli_fd)
{
perror("socket");
return -1;
}
//准备通信地址
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6789);
addr.sin_addr.s_addr = inet_addr("10.0.2.15");
socklen_t addrlen = sizeof(addr);
//链接服务器
if(connect(cli_fd,(struct sockaddr*)&addr,addrlen))
{
perror("connect");
return -1;
}
for(;;)
{
main_menu();
switch(get_cmd('0','4')-'0')
{
case M_EXIT: exit_bank(); break; // 退出
case M_OPEN: open_acc(); break; // 开户
case M_LOGIN: login(); continue; // 登陆
case M_DESTORY: destory(); break; // 销户
case M_UNLOCK: unlock(); break; // 解锁
}
char buf[BUF_SIZE];
//sprintf(buf,"%d %s %s %s %f %d",acc.type,acc.bank,acc.card,acc.pass,acc.balance,acc.islock);
acc_to_str(&acc,buf);
// 向服务端发送消息
int send_size = write(cli_fd,buf,strlen(buf)+1);
// 从服务端接收消息
int recv_size = read(cli_fd,buf,sizeof(buf));
// 显服务端执行的结果
puts(buf);
anykey_continue();
}
}
服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "bank.h"
#include "tools.h"
#define BUF_SIZE (4096)
//服务端socket对象
int svr_fd;
//银行账户后7位id
int id = 1234001;
//解析字符串到结构体中
void str_to_acc(Acc* acc,char* buf)
{
sscanf(buf,"%d %s %s %s %f %d",&acc->type,acc->bank,acc->card,acc->pass,&acc->balance,&acc->islock);
}
//显示数据
void show_acc(Acc* acc)
{
printf("%d %s %s %s %f %d\n",acc->type,acc->bank,acc->card,acc->pass,acc->balance,acc->islock);
}
//开户
void open_acc(Acc* acc,char* buf)
{
printf("%s\n",__func__);
// 获取银行卡号
sprintf(acc->bank,"19992000%d",id++);
// 设置初始密码
sprintf(acc->pass,"123456");
// 设置锁定状态
acc->islock = 0;
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
int fd = open(path,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,0644);
if(0 > fd)
{
sprintf(buf,"开户失败,服务器正在升级,请稍候!");
}
else
{
sprintf(buf,"开户成功,您的账号为:%s",acc->bank);
show_acc(acc);
write(fd,acc,sizeof(Acc));
close(fd);
}
}
//登陆
void login(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
printf("%s\n",path);
// 判断银行卡号是否正确
if(0 != access(path,F_OK))
{
sprintf(buf,"N:卡号不存在,请检查!");
return;
}
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,登陆失败!");
puts("登陆失败");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
show_acc(&local_acc);
if(3 <= local_acc.islock)
{
sprintf(buf,"N:登陆失败,帐号已经锁住!");
puts("登陆失败,帐号已经锁住!");
}
else if(0 != strcmp(local_acc.pass,acc->pass))
{
local_acc.islock++;
sprintf(buf,"N:登陆失败,帐号或者密码输错(您还有%d次机会)!",3-local_acc.islock);
printf("%s\n",acc->pass);
puts("登陆失败,密码输错!");
}
else
{
local_acc.islock = 0;
sprintf(buf,"Y:登陆成功");
puts("登陆成功");
}
fd = open(path,O_RDWR|O_TRUNC,0644);
write(fd,&local_acc,sizeof(Acc));
close(fd);
}
//销户
void destory(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
// 判断银行卡号是否正确
if(0 != access(path,F_OK))
{
sprintf(buf,"N:卡号不存在,请检查!");
return;
}
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,销户失败");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
show_acc(&local_acc);
close(fd);
if(strcmp(acc->card,local_acc.card))
{
sprintf(buf,"N:身份证号不正确,销户失败!");
return;
}
if(strcmp(acc->pass,local_acc.pass))
{
sprintf(buf,"N:密码不正确,销户失败!");
return;
}
if(remove(path))
{
perror("remove");
sprintf(buf,"N:服务器正在升级,销户失败!");
return;
}
sprintf(buf,"Y:销户成功,期待下次与你相遇!");
}
//解锁
void unlock(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
// 判断银行卡号是否正确
if(0 != access(path,F_OK))
{
sprintf(buf,"N:卡号不存在,请检查!");
return;
}
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,解锁失败");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
show_acc(&local_acc);
close(fd);
if(strcmp(acc->card,local_acc.card))
{
sprintf(buf,"N:身份证号不正确,解锁失败!");
return;
}
if(strcmp(acc->pass,local_acc.pass))
{
sprintf(buf,"N:密码不正确,解锁失败!");
return;
}
local_acc.islock = 0;
//lseek(fd,0,SEEK_SET);
fd = open(path,O_RDWR|O_TRUNC,0644);
write(fd,&local_acc,sizeof(Acc));
close(fd);
sprintf(buf,"Y:解锁成功!");
}
//存钱
void save(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,存款失败!");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
local_acc.balance += acc->balance;
//lseek(fd,0,SEEK_SET);
fd = open(path,O_RDWR|O_TRUNC,0644);
write(fd,&local_acc,sizeof(Acc));
close(fd);
sprintf(buf,"Y:存款成功,当前余额为:%.2f!",local_acc.balance);
}
//取钱
void take(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,取款失败!");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
if(local_acc.balance < acc->balance)
{
sprintf(buf,"N:余额不足,取款失败。当前余额为:%.2f!",local_acc.balance);
close(fd);
return;
}
local_acc.balance -= acc->balance;
//lseek(fd,0,SEEK_SET);
fd = open(path,O_RDWR|O_TRUNC,0644);
write(fd,&local_acc,sizeof(Acc));
close(fd);
sprintf(buf,"Y:取款成功,当前余额为:%.2f!",local_acc.balance);
}
//查询
void query(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
sprintf(buf,"N:服务器正在升级,查询失败!");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
close(fd);
sprintf(buf,"Y:查询成功,当前余额为:%.2f!",local_acc.balance);
}
//转帐
void transfer(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char src_path[256] = {} , dest_path[256];
sprintf(src_path,"%s%s",ACC_PATH,acc->bank);
sprintf(dest_path,"%s%s",ACC_PATH,acc->card);
// 判断银行卡号是否正确
if(0 != access(dest_path,F_OK))
{
sprintf(buf,"N:目标卡号不存在,请检查!");
return;
}
int src_fd = open(src_path,O_RDWR);
int dest_fd = open(dest_path,O_RDWR);
if(0 > src_fd || 0 > dest_fd)
{
error("open");
sprintf(buf,"N:服务器正在升级,转账失败");
return;
}
Acc src_acc = {} , dest_acc = {};
read(src_fd,&src_acc,sizeof(Acc));
read(dest_fd,&dest_acc,sizeof(Acc));
if(src_acc.balance < acc->balance)
{
sprintf(buf,"N:余额不足,转账失败。当前余额为:%.2f!",src_acc.balance);
return;
}
src_acc.balance -= acc->balance;
dest_acc.balance += acc->balance;
lseek(src_fd,0,SEEK_SET);
lseek(dest_fd,0,SEEK_SET);
write(src_fd,&src_acc,sizeof(Acc));
write(dest_fd,&dest_acc,sizeof(Acc));
close(src_fd);
close(dest_fd);
sprintf(buf,"Y:转账成功,当前余额为:%.2f!",src_acc.balance);
}
//修改密码
void repass(Acc* acc,char* buf)
{
printf("%s\n",__func__);
char path[256] = {};
sprintf(path,"%s%s",ACC_PATH,acc->bank);
int fd = open(path,O_WRONLY);
if(0 > fd)
{
error("open");
sprintf(buf,"N:服务器正在升级,查询失败!");
return;
}
Acc local_acc = {};
read(fd,&local_acc,sizeof(Acc));
strcpy(local_acc.pass,acc->pass);
write(fd,&local_acc,sizeof(Acc));
sprintf(buf,"Y:修改密码成功!");
}
//客户端线程
void* server(void* arg)
{
int cli_fd = *(int*)arg;
Acc* acc = malloc(sizeof(Acc));
for(;;)
{
char buf[BUF_SIZE];
int recv_size = read(cli_fd,buf,sizeof(buf));
if(0 >= recv_size || 0 == strcmp(buf,"quit"))
{
printf("客户端%d退出!\n",cli_fd);
free(acc);
close(cli_fd);
pthread_exit(NULL);
}
//解析字符串
str_to_acc(acc,buf);
//show_acc(acc);
//根据字符串type操作
switch(acc->type)
{
case M_OPEN: open_acc(acc,buf); break; //开户
case M_LOGIN: login(acc,buf); break; //登陆
case M_DESTORY: destory(acc,buf); break; //销户
case M_UNLOCK: unlock(acc,buf); break; //解锁
case M_SAVE: save(acc,buf); break; //存钱
case M_TAKE: take(acc,buf); break; //取钱
case M_QUERY: query(acc,buf); break; //查询
case M_TRANSFER: transfer(acc,buf); break;//转帐
case M_REPASS: repass(acc,buf); break; //改密码
}
write(cli_fd,buf,strlen(buf)+1);
}
}
//服务器退出
void sigint(int signum)
{
close(svr_fd);
printf("服务器关闭\n");
exit(0);
}
int main()
{
signal(SIGINT,sigint);
//创建socket对象
svr_fd = socket(AF_INET,SOCK_STREAM,0);
if(0 > svr_fd)
{
perror("socket");
return -1;
}
//准备通信地址
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6789);
addr.sin_addr.s_addr = inet_addr("10.0.2.15");
socklen_t addrlen = sizeof(addr);
//绑定socket对象与地址
if(bind(svr_fd,(struct sockaddr*)&addr,addrlen))
{
perror("bind");
return -1;
}
//设置监听
if(listen(svr_fd,10))
{
perror("listen");
return -1;
}
//等待链接
for(;;)
{
int cli_fd = accept(svr_fd,(struct sockaddr*)&addr,&addrlen);
if(0 > cli_fd)
{
printf("accept");
return -1;
}
pthread_t tid;
pthread_create(&tid,NULL,server,&cli_fd);
}
}