前面说到要使用asio库,需要编译datetime等库。现在使用asio库的时候,发现以前使用C语言实现一个简单的服务器模型时,代码是多么的冗长,想放到几个函数里面呢,觉得关系又比较密切,阅读不方便;放到一起吧又觉得代码太长。对asio库不了解,今天看了网上一个实现echo服务器的代码,很简洁。整个代码从建立服务器,到分发客户端数据,几行代码搞定。当然,首次接触asio和boost的人,对这段代码要理解清楚会费点神的。其中的bind的使用shared_ptr的使用,以及对asio机制的了解等。如果要理解这么短的代码实现原理,那肯定不是一天两天能够好的。至少需要去熟悉下boost里面的那么几个库吧。
曾经的我,作为一个服务的编程人员,只是那时是数据库开发;现在的我,作为一个客户端开发人员。对服务器建设这块都有很强的探索心里。一个强大的服务器是如何建立起来的,是如何实现业务分离的;如何面对大数据访问,安全问题如何解决等等。
一直知道公司的服务器是java做的,开始还以为公司可能这个数据比较小吧,但是前几天知道公司的在线交易服务系统也是JAVA程序员在维护着。刚好,这段时间一直想搞asio,就发现了LF领导者跟随者模式,半同步半异步模式什么的。还像还看到了这些模式的java调用接口,而是使用C++封装成中间件给JAVA调用。于是,我想。是不是做java服务端的人更容易熟悉服务器架构,因为他们每天都和这个东西打交道,出现问题没办法最后只能进入C++层找,OK,最后成为一个服务器C++高手,哈哈。
而就我身边的每一个C++开发者而言,都很希望能够做服务端开发,可似乎不如人意。换了几个工作,最后还是搞客户端。就其原因应该就是我们都希望自己找个公司去学习服务端开发,但一个招服务端开发的公司,对这个经验要求都比较高,与其招一个新手来慢慢摸索,不如公司内部其他语言的转来做更适合。毕竟,现在服务器大多数开发语言都是java活C#。
既然这样,那俺们就自己来搭建自己的服务器吧,从asio开始,一步一步的来。今天就把网上找的asio的HelloWorld程序贴上来吧.
服务器端的:
//下面是一个异步模式的简单的Tcp echo服务器
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
using namespace boost::asio;
using boost::system::error_code;
using ip::tcp;
struct CHelloWorld_Service
{
//类的初始化创建:设置io_service, 设置1000端口
CHelloWorld_Service(io_service &iosev)
:m_iosev(iosev),m_acceptor(iosev, tcp::endpoint(tcp::v4(), 1000))
{
}
//创建一个tcp的socket;且还是侦听
void start()
{
// 开始等待连接(非阻塞)
boost::shared_ptr<tcp::socket> psocket(new tcp::socket(m_iosev));
// 触发的事件只有error_code参数,所以用boost::bind把socket绑定进去
m_acceptor.async_accept(*psocket, boost::bind(&CHelloWorld_Service::accept_handler, this, psocket, _1) );
}
// 有客户端连接时accept_handler触发
void accept_handler(boost::shared_ptr<tcp::socket> psocket, error_code ec)
{
if(ec) return;
// 继续等待连接
start();
// 显示远程IP
std::cout << psocket->remote_endpoint().address() << std::endl;
// 发送信息(非阻塞)
boost::shared_ptr<std::string> pstr(new std::string("hello async world!"));
psocket->async_write_some(buffer(*pstr),
boost::bind(&CHelloWorld_Service::write_handler, this, pstr, _1, _2));
}
// 异步写操作完成后write_handler触发
void write_handler(boost::shared_ptr<std::string> pstr, error_code ec, size_t bytes_transferred)
{
if(ec)
std::cout<< "发送失败!" << std::endl;
else
std::cout<< *pstr << " 已发送" << std::endl;
}
private:
io_service &m_iosev;
ip::tcp::acceptor m_acceptor;
};
int main(int argc, char* argv[])
{
//建立io服务器
io_service iosev;
CHelloWorld_Service sev(iosev);
//开始侦听socket的连接;和开始接收远程数据
sev.start();
//开始执行回调函数
iosev.run();
return 0;
}
客户端的:
#include <iostream>
#include <boost/asio.hpp>
using namespace boost::asio;
int main(int argc, char* argv[])
{
// 所有asio类都需要io_service对象
io_service iosev;
// socket对象
ip::tcp::socket socket(iosev);
// 连接端点,这里使用了本机连接,可以修改IP地址测试远程连接
ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 1000);
// 连接服务器
boost::system::error_code ec;
socket.connect(ep,ec);
// 如果出错,打印出错信息
if(ec)
{
std::cout << boost::system::system_error(ec).what() << std::endl;
return -1;
}
// 接收数据
char buf[100];
size_t len=socket.read_some(buffer(buf), ec);
std::cout.write(buf, len);
return 0;
}
学习这东西,我一般推荐先抄袭的。不会不要急,要去看别人怎么做的,先会用。要用好当然要了解底层实现。当需要过程的,一个东西,不知道用它来做什么,就去看它是怎么实现的没意思。先盗版、然后才能给出自己的正版。哈哈!
如上面的这个服务器和客户端类,我们就需要去了解一些东西了:
io_service &m_iosev;//这里的io_service是何方神圣
ip::tcp::acceptor m_acceptor;//这里的ip::tcp::acceptor又为何物
如果看到这个代码的人没有接触过boost,那就更要为bind的使用而感到惊奇。
哎,真实学还无涯啊