简述:
初学BOOST库之Asio网络库,写一个基于TCP/IP网络协议的问题回答。一端是服务端(包含应答监听以及数据存储);
另一端为客户端运行起来之后服务器会响应,然后回答问题。(数据库是用ODBC API进行操作的)
1、服务端代码:
#include<iostream>
#include<boost/asio.hpp>
#include<vector>
#include <cstdlib>
#include<string>
#include <sql.h>
#include <sqlext.h>
#include <odbcss.h>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/array.hpp>
#include<boost/noncopyable.hpp>
#include<boost/shared_ptr.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/enable_shared_from_this.hpp>
#include<boost/algorithm/string.hpp>
#include <boost/aligned_storage.hpp>
using boost::asio::ip::tcp;
using namespace std;
#define MAXBUFLEN 255
#define MaxNameLen 20
#define SQLBINDCOL
SQLHENV henv = SQL_NULL_HENV;//定义环境句柄
SQLHDBC hdbc1 = SQL_NULL_HDBC;//定义数据库连接句柄
SQLHSTMT hstmt1 = SQL_NULL_HSTMT;//定义语句句柄
class handle_alloc
:private boost::noncopyable //禁止拷贝
{
private:
boost::aligned_storage<1024> st;
bool in;
public:
handle_alloc()
: in(false)
{}
void* allocate(std::size_t size)
{
if (!in&&st.size > size)
{
in = true;
return st.address();
}
else
{
return ::operator new(size); //实现不同的内存分配,应重载operator new,而不是new;若类中没有重载operator new,就调用的全局的::operator new堆分配
}
}
void* delallocate(void* p)
{
if (p == st.address())
{
in = false;
}
else
::operator delete(p);
}
};
template<class Thandle>
class usage_alloc_handle
{
private:
handle_alloc& alloc_;
Thandle handle_;
public:
usage_alloc_handle(handle_alloc& a, Thandle h)
:alloc_(a), handle_(h)
{
}
template<class A1>
void operator()(A1 a1)
{
handle_(a1);
}
template<class A1, class A2>
void operator()(A1 a1, A2 a2)
{
handle_(a1, a2);
}
friend void* asio_handle_alloc(std::size_t size, usage_alloc_handle<Thandle>* t_handle)
{
return t_handle->alloc_.allocate(size);
}
friend void asio_handle_delallocate(void*p, std::size_t size, usage_alloc_handle<Thandle>* t_handle)
{
t_handle->alloc_.delallocate(p);
}
};
template<class Thandle>
inline usage_alloc_handle<Thandle> mk_usage_alloc_handle(handle_alloc& a, Thandle h)
{
return usage_alloc_handle<Thandle>(a, h);
}
class io_service_pool
:private boost::noncopyable
{
public:
explicit io_service_pool(std::size_t p_size) //显式构造,不使用默认的隐式构造
:sio_service(0)
{
if (p_size == 0)
{
throw std::runtime_error("size is 0"); //抛出异常
}
for (std::size_t i = 0; i < p_size; ++i)
{
io_service_ptr io_service(new boost::asio::io_service);
work_ptr work(new boost::asio::io_service::work(*io_service));
vio_service.push_back(io_service);
vwork.push_back(work);
}
}
void run()
{
//std::vector<boost::shared_ptr<boost::thread>> threads_;
boost::thread_group tg;
for (std::size_t j = 0; j < vio_service.size(); ++j)
{
tg.create_thread([j, this]() {
for (;;) {
try {
this->vio_service[j]->run();
}
catch (...) {
cerr << "io_service throw exception" << endl;
}
}
});
//boost::shared_ptr<boost::thread>thread(new boost::thread(
//线程绑定io
// threads_.push_back(thread);
}
tg.join_all();
// for (std::size_t i = 0; i < threads_.size(); ++i)
// {
// threads_[i]->join();
// }
}
void stop()
{
for (std::size_t i = 0; i < vio_service.size(); ++i)
{
vio_service[i]->stop(); //停止事件循环(即停止run)
}
}
boost::asio::io_service& get_io_service()
{
boost::asio::io_service& io_service = *vio_service[sio_service];
++sio_service;
if (sio_service == vio_service.size())
{
sio_service = 0;
}
return io_service;
}
private:
typedef boost::shared_ptr<boost::asio::io_service> io_service_ptr;
typedef boost::shared_ptr<boost::asio::io_service::work> work_ptr; //用智能指针管理work,不用的时候可以用reset析构掉
std::size_t sio_service;
std::vector<io_service_ptr> vio_service;
std::vector<work_ptr> vwork;
};
class session //会话
:public boost::enable_shared_from_this<session> //weak_ptr获得this指针的shared_ptr,对象自己生产shared_ptr管理自己
//一个需要shared_ptr自我管理的类 普通的就这种格式
{
private:
tcp::socket _socket;
boost::asio::io_service& wio_service;
boost::array<char, 1024> _data;
handle_alloc _alloc;
public:
session(boost::asio::io_service& work_service, boost::asio::io_service& io_service)
:_socket(io_service), wio_service(work_service)
{}
tcp::socket& socket()
{
return _socket;
}
void start()
{
boost::system::error_code error; //系统错误码
handle_write(error);
_socket.async_read_some(boost::asio::buffer(_data), mk_usage_alloc_handle(_alloc, boost::bind(&session::handle_read,
shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));//异步错误处理
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
if (!error)
{
boost::shared_ptr<std::vector<char>>buf(new std::vector<char>);
buf->resize(bytes_transferred);
std::copy(_data.begin(), _data.begin() + bytes_transferred, buf->begin());
wio_service.post(boost::bind(&session::receive, shared_from_this(), buf, bytes_transferred));
_socket.async_read_some(boost::asio::buffer(_data), mk_usage_alloc_handle(_alloc, boost::bind(&session::receive,
shared_from_this(), buf, boost::asio::placeholders::bytes_transferred)));
}
}
void handle_write(const boost::system::error_code& error)
{
int i = 0;
if (!error)
{
string N[6] = { "\n\t\t\t\t\t\t\t简易问卷\t\n","please answer the questions\n", "\nyour name?\n","your age?\n","your school?\n","your address?\n" };
size_t m;
try
{
for (i = 0; i < 6; i++)
{
m = _socket.send(boost::asio::buffer(N[i]));
}
}
catch (std::exception& e)
{
std::cout << "exception:" << e.what() << std::endl;
}
if (m >= 0)
{
std::cout << "send:" << " " << N << std::endl;
}
}
}
int receive(boost::shared_ptr<std::vector<char>>buffers, size_t bytes_transferred)
{
SQLRETURN retcode;//错误返回码
retcode = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
if (retcode < 0)//错误处理
{
cout << "allocate ODBC Environment handle errors." << endl;
return -1;
}
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
if (retcode < 0) //错误处理
{
cout << "the ODBC is not version3.0 " << endl;
return -1;
}
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);
if (retcode < 0) //错误处理
{
cout << "allocate ODBC connection handle errors." << endl;
return -1;
}
char* szDSN = "mysql5.5";
char* szUID = "root";//log name
char* szAuthStr = "123";//passward
SQLSetConnectAttr(hdbc1, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
retcode = SQLConnect(hdbc1, (SQLCHAR*)szDSN, (SWORD)strlen(szDSN), (SQLCHAR*)szUID, (SWORD)strlen(szUID), (SQLCHAR*)szAuthStr, (SWORD)strlen(szAuthStr));
if (retcode < 0) //错误处理
{
cout << "connect to ODBC errors." << endl;
return -1;
}
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
if (retcode < 0) //错误处理
{
cout << "allocate ODBC statement handle errors." << endl;
return -1;
}
std::vector<string> v;
int j = 0;
char sttrr[10][10] = { 0 };
char* data_str = &(*buffers->begin());
boost::split(v, data_str, boost::is_any_of("#"), boost::token_compress_on);
for (int q = 0; q < v.size(), j < v.size(); q++, j++)
{
cout << v[q].c_str() << endl;
strcpy(sttrr[j], v[q].c_str());
}
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);//申请SQL语句句柄
SQLCHAR sql[] = "insert into asio_test values (?,?,?,?);";
SQLINTEGER P = SQL_NTS;
retcode = SQLPrepare(hstmt1, sql, SQL_NTS);
retcode = SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 20, 0, sttrr[0], 20, &P);//绑定参数
retcode = SQLBindParameter(hstmt1, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, sttrr[1], 10, &P);//绑定参数
retcode = SQLBindParameter(hstmt1, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, sttrr[2], 10, &P);//绑定参数
retcode = SQLBindParameter(hstmt1, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 20, 0, sttrr[3], 20, &P);//绑定参数
retcode = SQLExecute(hstmt1);//直接执行SQL语句
//SQLPOINTER
retcode = SQLExecDirect(hstmt1, (SQLCHAR*)"SElECT name,age,school,addr FROM asio_test", SQL_NTS);
if (retcode < 0)
{
cout << "Executing statement throught ODBC errors." << endl;
return -1;
}
SQLCHAR age[MaxNameLen + 1];
SQLCHAR name[MaxNameLen + 1];
SQLCHAR school[MaxNameLen + 1];
SQLCHAR addr[MaxNameLen + 1];
SQLINTEGER columnLen = 0; //数据库定义中该属性列的长度
#ifdef SQLBINDCOL
retcode = SQLBindCol(hstmt1, 1, SQL_C_CHAR, name, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 2, SQL_C_CHAR, age, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 3, SQL_C_CHAR, school, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 4, SQL_C_CHAR, addr, MaxNameLen, &columnLen);
while ((retcode = SQLFetch(hstmt1)) != SQL_NO_DATA)
{
if (columnLen > 0)
printf("name = %s age = %s school=%s addr=%s\n", name, age, school, addr);
else
printf("name = %s age = NULL school=NULL addr=NULL\n", name, age, school, addr);
}
#else
while (1)
{
retcode = SQLFetch(hstmt1);
if (retcode == SQL_NO_DATA)
break;
retcode = SQLGetData(hstmt1, 1, SQL_C_CHAR, name, MaxNameLen, &columnLen);
retcode = SQLGetData(hstmt1, 2, SQL_C_CHAR, city, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 3, SQL_C_CHAR, school, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 4, SQL_C_CHAR, addr, MaxNameLen, &columnLen);
if (columnLen > 0)
printf("name = %s age = %s school=%s addr=%s\n", name, age, school, addr);
else
printf("name = %s age = NULL school=NULL addr=NULL\n", name, age, school, addr);
}
#endif
SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);
SQLDisconnect(hdbc1);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc1);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return (0);
}
};
typedef boost::shared_ptr<session> s_ptr;
class server
{
private:
boost::shared_ptr<boost::thread> io_thread;
boost::shared_ptr<boost::thread> work_thread;
io_service_pool io_service_pool_;
io_service_pool io_service_work_pool;
tcp::acceptor _accept;
public:
server(short port, std::size_t io_service_pool_size)
:io_service_pool_(io_service_pool_size), io_service_work_pool(io_service_pool_size)
, _accept(io_service_pool_.get_io_service(), tcp::endpoint(tcp::v4(), 8080))
{
s_ptr new_session(new session(io_service_work_pool.get_io_service(), io_service_pool_.get_io_service()));
_accept.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}
void handle_accept(s_ptr new_session, const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session.reset(new session(io_service_work_pool.get_io_service(), io_service_pool_.get_io_service()));
_accept.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}
}
void run()
{
io_thread.reset(new boost::thread(boost::bind(&io_service_pool::run, &io_service_pool_)));
work_thread.reset(new boost::thread(boost::bind(&io_service_pool::run, &io_service_pool_)));
}
void stop()
{
io_service_pool_.stop();
io_service_work_pool.stop();
io_thread->join();
work_thread->join();
}
};
int main(int argc, char* argv[])
{
boost::asio::io_service ioservice;
boost::asio::ip::tcp::socket sock(ioservice);
try
{
std::cout << "server start." << std::endl;
server s(8080, 100);
s.run();
getchar();
s.stop();
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
2、客户端代码:
#include<iostream>
#include<boost/asio.hpp>
#include<vector>
using namespace boost::asio;
int main()
{
io_service ioservice;
ip::tcp::socket sock(ioservice);
ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 8080);
ioservice.run();
try
{
sock.connect(ep);
int i = 1;
while (i)
{
std::cout << "client start." << std::endl;
std::vector<char> str(1024, 0);
std::cout << "Recv from: " << sock.remote_endpoint().address() << std::endl;
int nSize = sock.read_some(buffer(str));
std::cout<< &str[0] << std::endl;
std::string sz ;
std::cout << "answer:(请用#键隔开每个答案)" << std::endl;
std::cin >> sz;
sock.send(buffer(sz));
i--;
}
sock.close();
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}