【boost学习】之boost::asio(3)——socket编程

  • Post author:
  • Post category:其他



asio的主要用途还是用于socket编程,本文就以一个tcp的daytimer服务为例简单的演示一下如何实现同步和异步的tcp socket编程。





一、同步服务器版本




客户端


客户端的代码如下:


//code of client
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using namespace std;
using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{
	try
	{
		//(1)通过tcp::socket类定义一个tcp client对象socket
		boost::asio::io_service io;
		tcp::socket socket(io);
		
		//(2)通过connect函数连接服务器,打开socket连接。
		tcp::endpoint end_point(boost::asio::ip::address::from_string("127.0.0.1"), 3200);
		socket.connect(end_point);

		for (;;)
		{
			boost::array<char, 128> buf;
			boost::system::error_code error;

			//(3)通过read_some函数来读数据
			size_t len = socket.read_some(boost::asio::buffer(buf), error);

			if (error == boost::asio::error::eof)
			{
				break;	//connection closed cleadly by peer
			}
			else if (error)
			{
				throw boost::system::system_error(error);	//some other error
			}

			cout.write(buf.data(), len);
		}
	}
	catch (std::exception& e)
	{
		cout<<e.what()<<endl;
	}
}

主要流程如下:

(1)通过tcp::socket类定义一个tcp client对象socket

(2)通过connect函数连接服务器,打开socket连接。

(3)通过read_some函数来读数据

另外,还可以通过write_some来写数据,通过close来关闭socket连接(这里是通过释放socket对象隐式释放连接)。





服务器


服务器代码如下:


//code of server
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>

using namespace std;
using namespace boost;
using boost::asio::ip::tcp;

int main(int argc, char*argv[])
{
	try
	{
		//(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)
		asio::io_service io;
		tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 3200));

		for (;;)
		{
			//(2)通过accept函数获取远端连接
			tcp::socket socket(io);
			acceptor.accept(socket);

			time_t now = time(0);
			string message = ctime(&now);

			//(3)通过远端连接的write_some函数将数据发往客户端
			system::error_code ignored_error;
			socket.write_some(asio::buffer(message), ignored_error);
		}
	}
	catch (std::exception &e)
	{
		cout<<e.what()<<endl;
	}
}

主要流程如下:

(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)

(2)通过accept函数获取远端连接

(3)通过远端连接的write_some函数将数据发往客户端







二、异步服务器


前面的服务器是同步版本,在大并发的场景下一般需要用到异步socket。服务器的异步版本如下:


//code of asyc server
#include <ctime>
#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;


void process_socket(shared_ptr<tcp::socket> socket)
{
	time_t now = time(0);
	shared_ptr<string> message(new string(ctime(&now)));

	auto callback = [=](const boost::system::error_code& err, size_t size)	//匿名函数:用到的任何外部变量都隐式按值捕获
	{
		if ((int)size == message->length())
		{
			cout<<"Write completed"<<endl;
		}
	};

	//(3)通过远端连接的write_some函数将数据发往客户端
	socket->async_send(boost::asio::buffer((*message)), callback);
}

typedef function<void (const boost::system::error_code&)> accept_callback;
void start_accept(tcp::acceptor& acceptor)
{
	shared_ptr<tcp::socket> socket(new tcp::socket(acceptor.get_io_service()));
	accept_callback callback = [&acceptor, socket](const boost::system::error_code& error)//匿名函数:socket按值捕获, acceptor按引用捕获
	{
		if (!error)
		{
			process_socket(socket);
		}

		//循环:相当于同步服务器中的for循环
		start_accept(acceptor);
	};

	//(2)通过accept函数获取远端连接
	acceptor.async_accept(*socket, callback);
}



int main()
{
	try
	{
		//(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)
		boost::asio::io_service io;
		tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 3200));

		start_accept(acceptor);

		//异步必须要主动调用io_service::run
		io.run();
	}
	catch (std::exception& e)
	{
		cout<<e.what()<<endl;
	}
}

这个异步版本的逻辑倒不是很复杂,基本上和.net中传统的异步socket相似,不过需要注意的是,由于c++中内存需要自己管理,而asio框架也没有提供任何管理机制,因此需要注意async_accept、async_send等函数的参数生命周期,切记不能在里面传入栈变量的引用。如果是堆变量,需要确保释放,本例中我是通过share_ptr来实现的自动释放。

更多的示例请参看asio官方文档。




【参考资料:


http://www.cnblogs.com/TianFang/archive/2013/02/02/2890529.html


关于匿名函数,参见:http://blog.csdn.net/augusdi/article/details/11773943