1、功能要求
包括两类用户:管理人员和普通用户(本文只写了普通用户程序)
普通用户功能:登录登出、存取款、转账、查询余额
2、技术要求
要求用到多进程多线程
要求同时允许多个用户操作(因为没有注册账号功能,且只初始化了两个账号信息,所以同时只能允许两个账号在线)
3、程序编写
第一次写C/S架构的程序,很多可能对于其他人来说很简单的问题我之前都没遇到过,所以写的过程中充满了艰辛,主要有几个问题让我困扰好久:
(1)客户端服务器之间的通信,整个socket框架借鉴的网上其他人的博客内容;
(2)技术要求中写道要用多进程多线程,一开始以为两个技术都要用到,纠结了老半天,网上查资料才明白选择一种应该就可以了,好像是因为两个技术同时使用容易出问题,具体也不太明白,我这里使用的多线程,需要注意的是在线程函数中传递参数的问题,具体可看程序或网上其他分析;
(3)socket编程客户端和服务器之间收发数据时,send和recv函数都是传送的一个数据缓冲区(const char FAR *buf),而银行系统中根据不同的操作,发送的数据类型和个数是不一样的,根据网上资料,本文将所有要发送或接受的数据定义在一个结构体中(分别是sendMsg和recvMsg),发送的时候可以直接发送结构体地址信息(sendMsg),且因为数据缓冲区(buf)传送的是字符串,最后一个字符为'\o',所以发送端的大小必须为(sizeof(sMsg)+1);在接收数据时,只能将接收到的数据存放在数据缓冲区(buf)中,然后再通过memcpy函数将其转换成对应的结构体(recvMsg),需要注意的是客户端和服务器中的结构体名称虽然可以不一样,但是两个结构体中对应的变量位置一定要相同,因为在发送和接收的时候,memcpy函数只把数据解析到对应位置。
服务器 server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#define SERVER_PORT 8888 //端口号,定义为宏方便以后直接修改
#define BACKLOG 10 //表服务器可以同时监测多少个客户端连接,设置为>0即可
struct accountMsg
{
//char *name;
int password;
int account;
int balance;//余额
//bool uCal;//账户类型
};
struct accountMsg accountMsg1[10];
struct recvMsg
{
//char *name; //户主姓名
int account; //账号
int password; //密码
int money; //金额
int uCal; //账户类型
int taskID; //操作任务类型
};
//—————————————————————————登录验证—————————————————————————————
int login(int iSocketClient,struct recvMsg rMsg)
{
int iSendLen;
int iRecvLen;
int accessBuf=0;
printf("____________login ing__________\n");
int uid;
for(uid=0;uid<10;uid++)
{
if(rMsg.account == accountMsg1[uid].account
&& rMsg.password == accountMsg1[uid].password )
{
accessBuf = 1;
break;
}
}
if (accessBuf !=1)
{
printf("__________login failed__________\n\n");
}
iSendLen = send(iSocketClient, &accessBuf, sizeof(accessBuf), 0);
if (iSendLen <= 0)
{
printf("__________message zend error__________\n\n");
close(iSocketClient);
return -1;
}
if(accessBuf == 1)
{
printf("__________client login succeed!__________\n\n");
accessBuf=0;
}
return uid;
}
//——————————————————————————存钱——————————————————————————
int saveMoney(int iSocketClient,struct recvMsg rMsg,int uid)
{
printf("___________save Money ing__________\n");
int iSendLen;
accountMsg1[uid].balance = accountMsg1[uid].balance + rMsg.money;
iSendLen = send(iSocketClient, &accountMsg1[uid].balance, sizeof(accountMsg1[uid].balance), 0);
if (iSendLen <= 0)
{
printf("___________save Money failed__________\n\n");
close(iSocketClient);
return -1;
}
printf("___________save Money succeed__________\n\n");
return 0;
}
//——————————————————————————取钱—————————————————————————————
int withdrawMoney(int iSocketClient,struct recvMsg rMsg,int uid)
{
printf("___________withdraw Money ing__________\n");
int iSendLen;
int balance;
balance = accountMsg1[uid].balance - rMsg.money;
iSendLen = send(iSocketClient, &balance, sizeof(balance), 0);
if (iSendLen <= 0)
{
printf("___________withdraw Money failed__________\n\n");
close(iSocketClient);
return -1;
}
else if(balance >= 0)
{
accountMsg1[uid].balance = balance;
}
printf("___________withdraw Money END__________\n\n");
return 0;
}
//——————————————————————————转账—————————————————————————————
int transferMoney(int iSocketClient,struct recvMsg rMsg,int uid)
{
printf("___________transfer Money ing__________\n");
int iSendLen;
int balance;
balance = accountMsg1[uid].balance - rMsg.money;
int tuid;
for(tuid=0;tuid<10;tuid++)
{
//核对转账账号是否存在
if(rMsg.account == accountMsg1[tuid].account && tuid != uid)
{
break;
}
}
//账号不存在
if(tuid>=10)
{
printf("__________account do not exit_________\n\n");
balance = -1;
}
iSendLen = send(iSocketClient, &balance, sizeof(balance), 0);
if (iSendLen <= 0)
{
printf("___________transfer Money failed__________\n\n");
close(iSocketClient);
return -1;
}
else if(balance >= 0 && tuid < 10)//send成功后再修改账户余额
{
accountMsg1[uid].balance = balance; //登录用户账号余额
accountMsg1[tuid].balance = accountMsg1[tuid].balance + rMsg.money; //被转账用户账号余额
}
printf("___________transfer Money END__________\n\n");
return 0;
}
int balanceEnquire(int iSocketClient,struct recvMsg rMsg,int uid)
{
printf("___________balance Enquire ing__________\n");
int iSendLen;
iSendLen = send(iSocketClient, &accountMsg1[uid].balance, sizeof(accountMsg1[uid].balance), 0);
if (iSendLen <= 0)
{
printf("___________balance Enquire failed__________\n\n");
close(iSocketClient);
return -1;
}
printf("___________balance Enquire succeed__________\n\n");
return 0;
}
//———————————————————————创建套接字——————————————————————————
int creatSocket()
{
int iSocketServer;
iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iSocketServer)
{
printf("socket error!\n");
return -1;
}
struct sockaddr_in tSocketServerAddr;//服务器地址结构
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);
int iRet;
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketServer, BACKLOG);
if (-1 == iRet)
{
printf("listen error!\n");
return -1;
}
return iSocketServer;
}
//——————————————————————线程处理函数——————————————————————————
void *handle_client(void *arg)
{
int iSocketClient = *(int*)arg;
int uid;
printf("_____________________at handle thread____________________\n");
while (1)
{
printf("____________________waiting client operation...____________________\n\n");
struct recvMsg rMsg;
memset(&rMsg,0,sizeof(rMsg));
char buf[1024]={0};
int iRecvLen = recv(iSocketClient, buf,1025, 0);
memcpy(&rMsg,buf,sizeof(rMsg));
if (iRecvLen <= 0)
{
printf("_________________error recv_______________\n");
break;
}
buf[iRecvLen] = '\0';
//printf("_____3 rMsg.account: %d rMsg.password: %d rMsg.taskID: %d___\n",rMsg.account,rMsg.password,rMsg.taskID);
switch(rMsg.taskID)
{
case 0: uid = login(iSocketClient,rMsg);
break;
case 1: saveMoney(iSocketClient,rMsg,uid);
break;
case 2: withdrawMoney(iSocketClient,rMsg,uid);
break;
case 3: transferMoney(iSocketClient,rMsg,uid);
break;
case 4: balanceEnquire(iSocketClient,rMsg,uid);
break;
default:
break;
}
}
//close(iSocketClient);
}
int main()
{
memset(accountMsg1, 0, sizeof(accountMsg1)*10);
accountMsg1[0].account = 123;
accountMsg1[0].password = 456;
accountMsg1[0].balance = 800;
//accountMsg1[0].uCal = 1;//普通用户
accountMsg1[1].account = 1212;
accountMsg1[1].password = 3456;
accountMsg1[1].balance = 500;
int iSocketServer;
iSocketServer=creatSocket();
if (-1 == iSocketServer)
{
printf("create socket failed!\n");
return -1;
}
struct sockaddr_in iSocketClientAddr;//客户端地址结构:当客户端来连接时会传过来
int iAddrLen;
int iRecvLen;
printf("____server running,waiting for client conneting...__\n");
while (1)
{
iAddrLen = sizeof(struct sockaddr);
int iSocketClient = accept(iSocketServer, (struct sockaddr *)&iSocketClientAddr, &iAddrLen);
if (-1 != iSocketClient)
{
printf("___accepted client message successed!__\n");
pthread_t pid;
pthread_create(&pid,NULL,handle_client,&iSocketClient);
pthread_detach(pid);
}
//close(iSocketServer);
}
close(iSocketServer);
return 0;
}
客户端 client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#define SERVER_PORT 8888
struct sendMsg
{
//char *name; //户主姓名
int account; //账号
int password; //密码
int money; //金额
int uCal; //账户类型
int taskID; //操作任务类型
};
int verify(int iSocketClient,struct sendMsg sMsg) //验证密码
{
printf("_________verify user info ing_______\n\n");
int iSendLen;
int iRecvLen;
iSendLen = send(iSocketClient, (char *)&sMsg, sizeof(sMsg)+1, 0);
//free(buf);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
//char accessBuf[20];
int accessBuf=0;
iRecvLen = recv(iSocketClient, &accessBuf, sizeof(accessBuf), 0);
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
if(0 == accessBuf)
{
return -1;
}
printf("_________verify user info succeed!_______\n\n");
return 0;
}
void login(int iSocketClient)
{
struct sendMsg sMsg;
loop0:printf("\n_____Welcome to bank client!_____\n\n");
memset(&sMsg,0,sizeof(sMsg));
loop1:printf("Please choose your identity:\n");
printf("0 Manager 1 User\n");
scanf("%d",&sMsg.uCal);
if(1 != sMsg.uCal)
{
if(0 != sMsg.uCal)
{
printf("identity error! \n");
goto loop1;
}
}
loop2:printf("account: ");
scanf("%d",&sMsg.account);
printf("password: ");
scanf("%d",&sMsg.password);
if(0 != verify(iSocketClient,sMsg))
{
printf("account or password error,please relogin! \n");
goto loop2;
}
while(1)
{
printf("_____________Welcome to bank client!_____________\n");
printf("_____________Main Business_____________\n\n");
printf("1 saveMoney \n2 withdrawMoney \n3 transferMoney \n4 balanceEnquire \n5 exit\n");
printf("\nPlease enter the business ID:");
scanf("%d",&sMsg.taskID);
printf("\n");
switch(sMsg.taskID)
{
case 1: saveMoney(iSocketClient,sMsg);
break;
case 2: withdrawMoney(iSocketClient,sMsg);
break;
case 3: transferMoney(iSocketClient,sMsg);
break;
case 4: balanceEnquire(iSocketClient,sMsg);
break;
case 5: goto loop0;
break;
default:
break;
}
}
}
int balanceEnquire(int iSocketClient,struct sendMsg sMsg)
{
int iSendLen;
int iRecvLen;
printf("___________balance Enquire ing__________\n\n");
iSendLen = send(iSocketClient, &sMsg, sizeof(sMsg), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
int balanceBuf;
iRecvLen = recv(iSocketClient, &balanceBuf, sizeof(balanceBuf), 0);
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
else
{
printf("Your balance is: %d\n\n",balanceBuf );
}
return 0;
}
int saveMoney(int iSocketClient,struct sendMsg sMsg)
{
int iSendLen;
int iRecvLen;
printf("___________save Money ing__________\n\n");
printf("please enter the money: ");
scanf("%d",&sMsg.money);
iSendLen = send(iSocketClient, &sMsg, sizeof(sMsg), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
int balanceBuf;
iRecvLen = recv(iSocketClient, &balanceBuf, sizeof(balanceBuf), 0);
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
else
{
printf("Save Succeed!\n Your balance is: %d\n\n",balanceBuf );
}
return 0;
}
int withdrawMoney(int iSocketClient,struct sendMsg sMsg)
{
int iSendLen;
int iRecvLen;
printf("___________withdraw Money ing__________\n\n");
printf("please enter the money: ");
scanf("%d",&sMsg.money);
iSendLen = send(iSocketClient, &sMsg, sizeof(sMsg), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
int balanceBuf;
iRecvLen = recv(iSocketClient, &balanceBuf, sizeof(balanceBuf), 0);
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
else if(balanceBuf < 0)
{
printf("withdraw failed!\n Your balance is not sufficient!\n\n" );
}
else
{
printf("withdraw Succeed!\n Your balance is: %d\n\n",balanceBuf );
}
return 0;
}
int transferMoney(int iSocketClient,struct sendMsg sMsg)
{
printf("___________transfer Money ing__________\n\n");
int iSendLen;
int iRecvLen;
printf("please enter payee's account: ");
scanf("%d",&sMsg.account);
printf("please enter the money: ");
scanf("%d",&sMsg.money);
iSendLen = send(iSocketClient, &sMsg, sizeof(sMsg), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
int balanceBuf;
iRecvLen = recv(iSocketClient, &balanceBuf, sizeof(balanceBuf), 0);
if (iRecvLen <= 0)
{
printf("___________transfer Money failed__________\n\n");
close(iSocketClient);
return -1;
}
else if(balanceBuf < 0)
{
printf("transfer failed!\n The account is wrong or your balance is not sufficient! \n\n" );
}
else
{
printf("transfer Succeed!\n Your balance is: %d\n\n",balanceBuf );
}
return 0;
}
int CreatSocekt()
{
int iSocketClient;
iSocketClient = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (0 == inet_aton("10.1.65.142", &tSocketServerAddr.sin_addr))
{
printf("Server IP error!\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
int iRet;
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("connect failed!!\n");
return -1;
}
return iSocketClient;
}
int main()
{
int iSocketClient = CreatSocekt();
if (-1 == iSocketClient)
{
printf("socket error!\n");
return -1;
}
login(iSocketClient);
close(iSocketClient);
return 0;
}
注:linux系统中,因为使用了多线程函数,所以编译时加上 -lpthread
gcc server.c -o server -lpthread
gcc client.c -o client -lpthread