銀行業務系統(c/s架構、socket網路程式設計、多執行緒)
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