一、简介
1、项目环境:Linux Ubuntu 16.04操作系统、C/C++语言
2、开发工具:Vim编辑器、g++4.8编译器、Makefile脚本、mysql数据库
3、技术关键:C++面向对象设计思想、基于TCP协议的网络编程技术、线程池技术
4、项目描述:项目基于Linux操作系统实现,采用C/S模式,使用TCP协议模拟ATM终端与服务器的通信过程。同时,本项目还在Server端创建了固定数量的线程池,用以减小创建线程时的开销,应对突发性大量请求。
二、项目目录结构

三、数据库设计
card.sql
create database card;
use card;
create table card_info(
_id_num char(20) primary key not null,
_name char(20) not null,
_card_num char(20),
_pswd char(8),
_money bigint
);
四、代码设计
myalgorithm.hpp
#pragma once
#include<string>
#include<vector>
vector<string> split_string(string str)
{
vector<string> ret;
string s;
for(int i = 2; i < str.size(); i++)
{
if(str[i] == ' ')
{
ret.push_back(s);
s.clear();
}
else
{
s += str[i];
if(i == str.size() - 1)
{
ret.push_back(s);
}
}
}
return ret;
}
tcp_socket.hpp
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<cassert>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
#define CHECK_RET(exp) if(!(exp)){return false;}
class tcp_socket
{
private:
int _fd;
public:
tcp_socket():_fd(-1)
{}
tcp_socket(int fd):_fd(fd)
{}
bool _socket()
{
_fd = socket(AF_INET, SOCK_STREAM, 0);
if(_fd < 0)
{
perror("socket");
return false;
}
return true;
}
bool _close() const
{
close(_fd);
return true;
}
bool _bind(const std::string& ip, uint16_t port) const
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
addr.sin_port = htons(port);
int ret = bind(_fd, (sockaddr *)&addr, sizeof(addr));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
bool _listen(int num) const
{
int ret = listen(_fd, num);
if(ret < 0)
{
perror("listen");
return false;
}
return true;
}
bool _accept(tcp_socket *peer, std::string *ip = nullptr, uint16_t *port = nullptr) const
{
sockaddr_in peer_addr;
socklen_t len = sizeof(peer_addr);
int new_sock = accept(_fd, (sockaddr *)&peer_addr, &len);
if(new_sock < 0)
{
perror("accept");
return false;
}
peer->_fd = new_sock;
if(ip != nullptr)
{
*ip = inet_ntoa(peer_addr.sin_addr);
}
if(port != nullptr)
{
*port = ntohs(peer_addr.sin_port);
}
return true;
}
bool _recv(std::string *buf) const
{
buf->clear();
char tmp[1024 * 10] = {};
ssize_t read_size = recv(_fd, tmp, sizeof(tmp), 0);
if(read_size < 0)
{
perror("recv");
return false;
}
if(read_size == 0)
{
return false;
}
buf->assign(tmp, read_size);
return true;
}
bool _send(const std::string& buf) const
{
ssize_t write_size = send(_fd, buf.data(), buf.size(), 0);
if(write_size < 0)
{
perror("send");
return false;
}
return true;
}
bool _connect(const std::string& ip, uint16_t port) const
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
addr.sin_port = htons(port);
int ret = connect(_fd, (sockaddr *)&addr, sizeof(addr));
if(ret < 0)
{
perror("connect");
return false;
}
return true;
}
int _get_fd() const
{
return _fd;
}
};
thread_pool.hpp
#pragma once
#include"../include/tcp_socket.hpp"
#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>
#include<time.h>
#include<functional>
#define MAX_THREAD 5
typedef std::function<void (const std::string& req, std::string *resp)> Handler;
struct thread_arg
{
tcp_socket new_sock;
std::string ip;
uint16_t port;
Handler handler;
};
typedef bool (*handler_t)(thread_arg *);
using namespace std;
class ThreadTask
{
private:
thread_arg *_data;
handler_t _handler;
public:
ThreadTask(thread_arg *data, handler_t handler)
:_data(data),_handler(handler)
{}
void setTask(thread_arg *data, handler_t handler)
{
_data = data;
_handler = handler;
}
void run()
{
_handler(_data);
}
};
class ThreadPool
{
private:
int _thread_max;
int _thread_cur;
bool _tp_quit;
queue<ThreadTask *> _task_queue;
pthread_mutex_t _lock;
pthread_cond_t _cond;
private:
void lockQueue()
{
pthread_mutex_lock(&_lock);
}
void unlockQueue()
{
pthread_mutex_unlock(&_lock);
}
void wakeUpOne()
{
pthread_cond_signal(&_cond);
}
void wakeUpAll()
{
pthread_cond_broadcast(&_cond);
}
void threadQuit()
{
--_thread_cur;
unlockQueue();
pthread_exit(nullptr);
}
void threadWait()
{
if(_tp_quit)
{
threadQuit();
}
pthread_cond_wait(&_cond, &_lock);
}
bool isEmpty()
{
return _task_queue.empty();
}
static void * thr_start(void * arg)
{
ThreadPool * tp = static_cast<ThreadPool *>(arg);
while(1)
{
tp->lockQueue();
while(tp->isEmpty())
{
tp->threadWait();
}
ThreadTask * tt;
tp->popTask(&tt);
tp->unlockQueue();
tt->run();
delete tt;
}
return nullptr;
}
public:
ThreadPool(int max = MAX_THREAD)
:_thread_max(max),
_thread_cur(max),
_tp_quit(false)
{
pthread_mutex_init(&_lock, nullptr);
pthread_cond_init(&_cond, nullptr);
}
~ThreadPool()
{
pthread_mutex_destroy(&_lock);
pthread_cond_destroy(&_cond);
}
bool poolInit()
{
pthread_t tid;
for(int i = 0; i < _thread_max; ++i)
{
int ret = pthread_create(&tid, nullptr, thr_start, this);
if(ret != 0)
{
cout<<"create thread pool error..."<<endl;
return false;
}
}
return true;
}
bool pushTask(ThreadTask * tt)
{
lockQueue();
if(_tp_quit)
{
unlockQueue();
return false;
}
_task_queue.push(tt);
wakeUpOne();
unlockQueue();
return true;
}
bool popTask(ThreadTask ** tt)
{
*tt = _task_queue.front();
_task_queue.pop();
return true;
}
bool poolQuit()
{
lockQueue();
_tp_quit = true;
unlockQueue();
while(_thread_cur > 0)
{
wakeUpAll();
sleep(1);
}
return true;
}
};
tcp_multithread_server.hpp
#pragma once
#include"thread_pool.hpp"
#include<functional>
#include<pthread.h>
class tcp_multithread_server
{
private:
tcp_socket _listen_sock;
std::string _ip;
uint16_t _port;
ThreadPool pool;
public:
tcp_multithread_server(const std::string& ip, uint16_t port):_ip(ip),_port(port)
{
pool.poolInit();
}
static bool _thread_entry(thread_arg *data)
{
thread_arg *p = data;
_process_connect(p);
p->new_sock._close();
delete p;
return true;
}
static void _process_connect(thread_arg *arg)
{
while(true)
{
std::string req;
if(!arg->new_sock._recv(&req))
{
printf("[client %s:%d] disconnected...\n", arg->ip.c_str(), arg->port);
break;
}
std::string resp;
arg->handler(req, &resp);
arg->new_sock._send(resp);
printf("[client %s:%d] req:%s, resp:%s\n", arg->ip.c_str(), arg->port, req.c_str(), resp.c_str());
}
}
bool _start(Handler handler)
{
CHECK_RET(_listen_sock._socket());
CHECK_RET(_listen_sock._bind(_ip, _port));
CHECK_RET(_listen_sock._listen(5));
while(true)
{
thread_arg *arg = new thread_arg();
arg->handler = handler;
if(!_listen_sock._accept(&arg->new_sock, &arg->ip, &arg->port))
{
continue;
}
printf("[client %s:%d] connect...\n", arg->ip.c_str(), arg->port);
ThreadTask * tt = new ThreadTask(arg, _thread_entry);
pool.pushTask(tt);
}
pool.poolQuit();
return true;
}
};
tcp_client.hpp
#pragma once
#include"../include/tcp_socket.hpp"
class tcp_client
{
private:
tcp_socket _sock;
std::string _ip;
uint16_t _port;
public:
tcp_client(const std::string& ip, uint16_t port):_ip(ip),_port(port)
{
_sock._socket();
}
~tcp_client()
{
_sock._close();
}
bool _connect()
{
return _sock._connect(_ip, _port);
}
bool _recv(std::string *buf)
{
return _sock._recv(buf);
}
bool _send(const std::string& buf)
{
return _sock._send(buf);
}
};
card_info.hpp
#pragma once
#include<string>
#include<stdio.h>
using namespace std;
class card_info
{
private:
std::string _id_num;
std::string _name;
std::string _card_num;
std::string _pswd;
long _money;
public:
card_info()
{}
card_info(const string& id_num, const string& name, const string& card_num = "", const string& pswd = "", const long& money = 0)
:_id_num(id_num),_name(name),_card_num(card_num),_pswd(pswd),_money(money)
{}
void set_id_num(const string& id_num)
{
_id_num = id_num;
}
void set_name(const string& name)
{
_name = name;
}
void set_card_num(const string& card_num)
{
_card_num = card_num;
}
void set_pswd(const string& pswd)
{
_pswd = pswd;
}
void set_money(const long& money)
{
_money = money;
}
string get_id_num()
{
return _id_num;
}
string get_name()
{
return _name;
}
string get_card_num()
{
return _card_num;
}
string get_pswd()
{
return _pswd;
}
long get_money()
{
return _money;
}
string toString()
{
char str[1024];
sprintf(str, "%s %s %s %s %ld", get_id_num().c_str(), get_name().c_str(), get_card_num().c_str(), get_pswd().c_str(), get_money());
string ret(str);
return ret;
}
};
mysql_con.hpp
#pragma once
#include<my_global.h>
#include<mysql.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>
#include"../include/card_info.hpp"
#include"../include/myalgorithm.hpp"
using namespace std;
static const char * host = "localhost";
static const char * user = "root";
static const char * password = "123456";
static const char * database = "card";
class mysql_oper
{
private:
MYSQL * con;
private:
static void finish_with_error(MYSQL * con)
{
fprintf(stderr, "MYSQL Running Error: %s\n", mysql_error(con));
if(con)
{
mysql_close(con);
}
exit(EXIT_FAILURE);
}
void query_result(vector<string>& ret)
{
MYSQL_RES * res = mysql_store_result(con);
if(res == nullptr)
{
finish_with_error(con);
}
int num_fields = mysql_num_fields(res);
cout<<"rows count:"<<num_fields<<endl;
MYSQL_ROW row;
while(row = mysql_fetch_row(res))
{
string s;
for(int i = 0; i < num_fields; ++i)
{
//cout<<row[i]<<" ";
s += row[i];
s += " ";
}
ret.push_back(s);
//cout<<endl;
}
mysql_free_result(res);
}
public:
mysql_oper()
{
con = mysql_init(nullptr);
if(con == nullptr)
{
finish_with_error(con);
}
if(mysql_real_connect(con, host, user, password, database, 0, nullptr, 0) == nullptr)
{
finish_with_error(con);
}
if(mysql_set_character_set(con, "utf8") != 0)
{
finish_with_error(con);
}
}
~mysql_oper()
{
mysql_close(con);
}
void _insert(const string& id_num, const string& name, const string& card_num = "", const string& pswd = "", const long& money = 0)
{
char insert[1000];
sprintf(insert, "insert into card_info values('%s', '%s', '%s', '%s', %ld)", id_num.c_str(), name.c_str(), card_num.c_str(), pswd.c_str(), money);
cout<<insert<<endl;
if(mysql_query(con, insert))
{
finish_with_error(con);
}
}
vector<string> _selectAll()
{
char query[1000];
sprintf(query, "select * from card_info");
cout<<query<<endl;
if(mysql_query(con, query))
{
finish_with_error(con);
}
vector<string> ret;
query_result(ret);
return ret;
}
string _select_cond_card_num_judge_repet(string card_num)
{
string ret("");
vector<string> all = _selectAll();
for(int i = 0; i < all.size(); i++)
{
vector<string> tmp = split_string(all[i]);
if(tmp[2] == card_num)
{
ret = all[i];
break;
}
}
return ret;
}
string _select_cond_card_num_find_pswd(string card_num)
{
string ret("");
vector<string> all = _selectAll();
for(int i = 0; i < all.size(); i++)
{
vector<string> tmp = split_string(all[i]);
if(tmp[2] == card_num)
{
ret = tmp[3];
return ret;
}
}
return ret;
}
string _select_cond_card_num_find_balance(string card_num)
{
string ret("");
vector<string> all = _selectAll();
for(int i = 0; i < all.size(); i++)
{
vector<string> tmp = split_string(all[i]);
if(tmp[2] == card_num)
{
ret = tmp[4];
return ret;
}
}
return ret;
}
string _update_cond_card_num_desposit(string card_num, string money)
{
string ret("");
char update[1000];
sprintf(update, "update card_info set _money = _money + %d where _card_num = %s", atoi(money.c_str()), card_num.c_str());
cout<<update<<endl;
if(mysql_query(con, update))
{
finish_with_error(con);
return ret;
}
return card_num;
}
};
display.hpp
#pragma once
#include"../include/card_info.hpp"
#include"tcp_client.hpp"
#include<iostream>
#include<unistd.h>
using namespace std;
class screen_display
{
public:
void _register()
{
system("clear");
cout<<"-----------------------------------------------------------------------"<<endl;
cout<<"| |"<<endl;
cout<<"| WELCOME TO ATM! PLEASE OPERATION WITH TIPS! |"<<endl;
cout<<"| |"<<endl;
cout<<"|---------------------------------------------------------------------|"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT MESSAGE FOLLOW |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| 1.ID CARD NUMBER |"<<endl;
cout<<"| 2.YOUR NAME |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT! |"<<endl;
cout<<"-----------------------------------------------------------------------"<<endl;
}
void _work()
{
system("clear");
cout<<"-----------------------------------------------------------------------"<<endl;
cout<<"| |"<<endl;
cout<<"| WELCOME TO ATM! PLEASE OPERATION WITH TIPS! |"<<endl;
cout<<"| |"<<endl;
cout<<"|---------------------------------------------------------------------|"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT MESSAGE FOLLOW |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| 1.Check Balance 3.Transfer |"<<endl;
cout<<"| 2.Deposit 4.Withdraw |"<<endl;
cout<<"| 0.Exit |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT! |"<<endl;
cout<<"-----------------------------------------------------------------------"<<endl;
}
void _login()
{
system("clear");
cout<<"-----------------------------------------------------------------------"<<endl;
cout<<"| |"<<endl;
cout<<"| WELCOME TO ATM! PLEASE OPERATION WITH TIPS! |"<<endl;
cout<<"| |"<<endl;
cout<<"|---------------------------------------------------------------------|"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT MESSAGE FOLLOW |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| 1.ID CARD NUMBER |"<<endl;
cout<<"| 2.YOUR PASSWORD |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT! |"<<endl;
cout<<"-----------------------------------------------------------------------"<<endl;
}
void _error()
{
cout<<"input error!"<<endl;
sleep(1);
}
void display()
{
system("clear");
cout<<"-----------------------------------------------------------------------"<<endl;
cout<<"| |"<<endl;
cout<<"| WELCOME TO ATM! PLEASE OPERATION WITH TIPS! |"<<endl;
cout<<"| |"<<endl;
cout<<"|---------------------------------------------------------------------|"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| A Register |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| B Login |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| |"<<endl;
cout<<"| PLEASE INPUT! |"<<endl;
cout<<"-----------------------------------------------------------------------"<<endl;
cout<<">>";
}
};
atm_server.cpp
#include"tcp_multithread_server.hpp"
#include"../include/card_info.hpp"
#include"../include/myalgorithm.hpp"
#include"../mysql/mysql_con.hpp"
#include<string>
#include<vector>
#include<time.h>
#include<stdlib.h>
using namespace std;
void handler(const std::string& req, std::string *resp)
{
char ch = req[0];
if(ch == 'A')
{
vector<string> split_req = split_string(req);
srand((unsigned long)time(nullptr));
long new_card_num = 1000000000 + rand() % 1000000000;
char ch[20];
sprintf(ch, "%ld", new_card_num);
string tmp(ch), spa("");
mysql_oper mo;
while(mo._select_cond_card_num_judge_repet(tmp) != spa)
{
new_card_num = 1000000000 + rand() % 1000000000;
sprintf(ch, "%ld", new_card_num);
tmp.clear();
tmp = ch;
}
string init_pswd("000000");
mo._insert(split_req[0], split_req[1], tmp, init_pswd, 0);
*resp = tmp;
}
else if(ch == 'B')
{
vector<string> split_req = split_string(req);
mysql_oper mo;
string res = mo._select_cond_card_num_find_pswd(split_req[0]);
if(res == split_req[1])
{
*resp = split_req[0];
}
else
{
string tmp("None");
*resp = tmp;
}
}
else if(ch == '1')
{
vector<string> split_req = split_string(req);
mysql_oper mo;
string res = mo._select_cond_card_num_find_balance(split_req[0]);
*resp = res;
}
else if(ch == '2')
{
vector<string> split_req = split_string(req);
mysql_oper mo;
string res = mo._update_cond_card_num_desposit(split_req[0], split_req[1]);
if(res == split_req[0])
{
string succ("Successful!");
*resp = succ;
}
else
{
string fail("Failured!");
*resp = fail;
}
}
else if(ch == '3')
{
vector<string> split_req = split_string(req);
mysql_oper mo;
string row, spa("");
if((row = mo._select_cond_card_num_judge_repet(split_req[1])) != spa)
{
string res_a = mo._update_cond_card_num_desposit(split_req[1], split_req[2]);
string sub = "-" + split_req[2];
string res_s = mo._update_cond_card_num_desposit(split_req[0], sub);
if(res_a == split_req[1] && res_s == split_req[0])
{
string succ("Successful!");
*resp = succ;
}
else
{
string fail("Failured!");
*resp = fail;
}
}
}
else if(ch == '4')
{
vector<string> split_req = split_string(req);
mysql_oper mo;
string res = mo._update_cond_card_num_desposit(split_req[0], split_req[1]);
if(res == split_req[0])
{
string succ("Successful!");
*resp = succ;
}
else
{
string fail("Failured!");
*resp = fail;
}
}
return;
}
int main(int argc, char *argv[])
{
if(argc < 0)
{
printf("Use ./dict_server [IP] [PORT]\n");
return 1;
}
tcp_multithread_server server(argv[1], atoi(argv[2]));
server._start(handler);
return 0;
}
atm_client.cpp
#include"display.hpp"
#include<iostream>
using namespace std;
int main(int argc, char * argv[])
{
if(argc != 3)
{
cout<<"Use Format './dict_client [IP] [PORT]'"<<endl;
return 1;
}
while(true)
{
tcp_client client(argv[1], atoi(argv[2]));
if(!client._connect())
{
return 1;
}
screen_display sd;
string input;
sd.display();
cin>>input;
if(input == "A")
{
sd._register();
string id_num, name;
cout<<"ID Card Number >> ";
cin>>id_num;
cout<<"Your Name >> ";
cin>>name;
string req = input + "|" + id_num + " " + name;
client._send(req);
string res;
client._recv(&res);
cout<<"Your ID Card Number : "<<res<<endl;
sleep(10);
}
else if(input == "B")
{
sd._login();
string id_num, pswd;
cout<<"ID Card Number >> ";
cin>>id_num;
cout<<"Your Password >> ";
cin>>pswd;
string req = input + "|" + id_num + " " + pswd;
client._send(req);
string res, tmp("None");
client._recv(&res);
while(true)
{
if(res == id_num)
{
sd._work();
string choose;
cout<<"Please Choose Service >> ";
cin>>choose;
if(choose == "0")
{
cout<<"exit..."<<endl;
sleep(2);
break;
}
else if(choose == "1")
{
string req = choose + "|" + id_num;
client._send(req);
string res;
client._recv(&res);
cout<<"Your Balance : "<<res<<endl;
sleep(5);
}
else if(choose == "2")
{
string bal;
cout<<"Please Put Money >> ";
cin>>bal;
string req = choose + "|" + id_num + " " + bal;
client._send(req);
string res;
client._recv(&res);
cout<<"Desposit "<<res<<endl;
sleep(5);
}
else if(choose == "3")
{
string card, mon;
cout<<"Please Input Chamber Payee Card Number >> ";
cin>>card;
cout<<"Please Input Transfer Money >> ";
cin>>mon;
string req = choose + "|" + id_num + " " + card + " " + mon;
client._send(req);
string res;
client._recv(&res);
cout<<"Transfer "<<res<<endl;
sleep(5);
}
else if(choose == "4")
{
string bal;
cout<<"Please Input Withdraw Money >> ";
cin>>bal;
string req = choose + "|" + id_num + " " + "-" + bal;
client._send(req);
string res;
client._recv(&res);
cout<<"Withdraw "<<res<<endl;
sleep(5);
}
else
{
sleep(2);
break;
}
}
else
{
sleep(2);
break;
}
}
}
else
{
sd._error();
continue;
}
}
return 0;
}
Makefile脚本
.phony:all
all:./bin/atm_client ./bin/atm_server
./bin/atm_client:./client_ATM/atm_client.cpp
g++-4.8 -std=c++11 -o $@ $^
./bin/atm_server:./server_ATM/atm_server.cpp
g++-4.8 -std=c++11 -o $@ $^ -lpthread `mysql_config --cflags --libs`
.phony:clean
clean:
rm -rf ./bin/atm_client ./bin/atm_server
本文档介绍了一个在Linux Ubuntu 16.04上使用C++和面向对象设计实现的ATM模拟项目。项目利用TCP协议进行网络编程,采用线程池技术来处理来自客户端的请求,通过MySQL数据库存储卡信息。主要文件包括TCP套接字、线程池、服务器和客户端的实现。

7171

被折叠的 条评论
为什么被折叠?



