Daytime.1 - A synchronous TCP daytime client
一个同步的TCP daytime客户端
This tutorial program shows how to use asio to implement a client application with TCP.
We start by including the necessary header files.
本示例程序显示如何使用Asio来实现一个TCP客户端程序。
让我们从添加必需的头文件开始。
#include
<
iostream
>
#include
<
boost
/
array.hpp
>
#include
<
boost
/
asio.hpp
>
The purpose of this application is to access a daytime service, so we need the user to specify the server.
这个应用程序的目的是访问一个daytime服务器,因此我们需要用户去指定服务器。(如time-nw.nist.gov,用IP亦可)
using
boost::asio::ip::tcp;
int
main(
int
argc,
char
*
argv[])
{
try
{
if
(argc
!=
2
)
{
std::cerr
<<
"
Usage: client <host>
"
<<
std::endl;
return
1
;
}
All programs that use asio need to have at least one boost::asio::io_service object.
所有使用asio的程序都至少需要一个boost::asio::io_service对象。
boost::asio::io_service io_service;
We need to turn the server name that was specified as a parameter to the application, into a TCP endpoint. To do this we use an boost::asio::ip::tcp::resolver object.
我们需要把服务器的名称转化为TCP的节点,而该名称是通过应用程序的参数指定的。我们使用boost::asio::ip::tcp::resolver 对象来完成。
tcp::resolver resolver(io_service);
A resolver takes a query object and turns it into a list of endpoints. We construct a query using the name of the server, specified in argv[1]
, and the name of the service, in this case "daytime"
.
一个resolver对象获得一个query对象,并将其转换为节点列表.我们通过argv[1]中的服务器名称和服务名,在这里是daytime,构造一个query。
tcp::resolver::query query(argv[ 1 ], " daytime " );
The list of endpoints is returned using an iterator of type boost::asio::ip::tcp::resolver::iterator. A default constructed boost::asio::ip::tcp::resolver::iterator object is used as the end iterator.
节点列表用 boost::asio::ip::tcp::resolver::iterator 类型的迭代器返回。返回的iterator将采用boost::asio::ip::tcp::resolver::iterator的默认构造函数来构造。
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
Now we create and connect the socket. The list of endpoints obtained above may contain both IPv4 and IPv6 endpoints, so we need to try each of them until we find one that works. This keeps the client program independent of a specific IP version.
现在我们建立一个socket并连接之,由于获得的节点既有IPv4也有IPv6的。所以,我们需要依次尝试访问它们直到找到一个可以正常工作的。这样做可使得我们的程序独立于特定的IP版本。
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect( * endpoint_iterator ++ , error);
}
if (error)
throw boost::system::system_error(error);
The connection is open. All we need to do now is read the response from the daytime service.
We use a boost::array
to hold the received data. The boost::asio::buffer() function automatically determines the size of the array to help prevent buffer overruns. Instead of a boost::array
, we could have used a char []
or std::vector
.
连接打开后,现在我们需要做的就是读取daytime服务器的响应。
我们使用boost::array 来存放接收到的数据。boost::asio::buffer()函数会自动确定array的长度来防止缓冲区溢出。我们也可以使用 char []
或 std::vector来代替boost::array。
for (;;)
{
boost::array < char , 128 > buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
When the server closes the connection, the boost::asio::ip::tcp::socket::read_some() function will exit with the boost::asio::error::eof error, which is how we know to exit the loop.
当服务器关闭连接时,boost::asio::ip::tcp::socket::read_some()函数会以boost::asio::error::eof错误标志返回, 通过该错误标志,我们知道应该退出循环了。
if (error == boost::asio::error::eof)
break ; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
}
Finally, handle any exceptions that may have been thrown.
最后,处理所有可能抛出的异常 。
}
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
See the full source listing
完整代码:
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt )
//
#include < iostream >
#include < boost / array.hpp >
#include < boost / asio.hpp >
using boost::asio::ip::tcp;
int main( int argc, char * argv[])
{
try
{
if (argc != 2 )
{
std::cerr << " Usage: client <host> " << std::endl;
return 1 ;
}
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(argv[ 1 ], " daytime " );
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect( * endpoint_iterator ++ , error);
}
if (error)
throw boost::system::system_error(error);
for (;;)
{
boost::array < char , 128 > buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break ; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
}
}
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
return 0 ;
}
Daytime.2 - A synchronous TCP daytime server
一个同步的TCP daytime服务器
This tutorial program shows how to use asio to implement a server application with TCP.
本示例示范如何使用Asio来实现一个TCP服务器程序。
#include
<
ctime
>
#include
<
iostream
>
#include
<
string
>
#include
<
boost
/
asio.hpp
>
using
boost::asio::ip::tcp;
We define the function make_daytime_string()
to create the string to be sent back to the client. This function will be reused in all of our daytime server applications.
我们先定义一个make_daytime_string()来产生需要发送给客户端的字符串.这个函数会在我们所有的daytime服务器上被使用。
std::
string
make_daytime_string()
{
using
namespace
std;
//
For time_t, time and ctime;
time_t now
=
time(
0
);
return
ctime(
&
now);
}
int
main()
{
try
{
boost::asio::io_service io_service;
A boost::asio::ip::tcp::acceptor object needs to be created to listen for new connections. It is initialised to listen on TCP port 13, for IP version 4.
新建一个asio::ip::tcp::acceptor对象来监听新的连接。该对象应遵守IPv4协议,监听TCP端口13。
This is an iterative server, which means that it will handle one connection at a time. Create a socket that will represent the connection to the client, and then wait for a connection.
这是一个iterative server,也就是说同一时间只能处理一个连接。建立一个表示与客户端的连接的socket, 然后等待客户端的连接。
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
A client is accessing our service. Determine the current time and transfer this information to the client.
std:: string message = make_daytime_string();
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message),
boost::asio::transfer_all(), ignored_error);
}
}
Finally, handle any exceptions.
最后,
处理异常。
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
return 0 ;
}
See the full source listing
全部源码:
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt )
//
#include < ctime >
#include < iostream >
#include < string >
#include < boost / asio.hpp >
using boost::asio::ip::tcp;
std:: string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time( 0 );
return ctime( & now);
}
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13 ));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
std:: string message = make_daytime_string();
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message),
boost::asio::transfer_all(), ignored_error);
}
}
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
return 0 ;
}
Daytime.3 - An asynchronous TCP daytime server
The main() function
主函数
int
main()
{
try
{
We need to create a server object to accept incoming client connections. The boost::asio::io_service object provides I/O services, such as sockets, that the server object will use.
我们需要创建一个服务器对象,用来接受客户端的连接。boost::asio::io_service对象提供了像sockets这样的I/O服务,这些服务都是服务器对象将要使用的。
boost::asio::io_service io_service;
tcp_server server(io_service);
Run the boost::asio::io_service object so that it will perform asynchronous operations on your behalf.
运行boost::asio::io_service 对象,它将执行你想要的异步操作。
io_service.run();
}
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
return 0 ;
}
The tcp_server class
TCP服务器类
class
tcp_server
{
public
:
The constructor initialises an acceptor to listen on TCP port 13.
构造函数初始化一个用于监听TCP 端口13的接收器。
tcp_server(boost::asio::io_service & io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 13 ))
{
start_accept();
}
private :
The function start_accept()
creates a socket and initiates an asynchronous accept operation to wait for a new connection.
函数start_accept()创建一个socket ,同时启动一个异步接收操作去等待一个新的连接。
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.io_service());
acceptor_.async_accept(new_connection -> socket(),
boost::bind( & tcp_server::handle_accept, this , new_connection,
boost::asio::placeholders::error));
}
The function handle_accept()
is called when the asynchronous accept operation initiated by start_accept()
finishes. It services the client request, and then calls start_accept()
to initiate the next accept operation.
当start_accept()启动的异步接收操作完成后,handle_accept()函数将被调用。它响应客户端的请求,然后调用start_accept()函数去启动另一个接收操作。
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code & error)
{
if ( ! error)
{
new_connection -> start();
start_accept();
}
}
The tcp_connection class
TCP连接类
We will use shared_ptr
and enable_shared_from_this
because we want to keep the tcp_connection
object alive as long as there is an operation that refers to it.
我们希望只要还有一个操作涉及tcp_connection对象,该对象就是有效的。因此我们使用shared_ptr 和enable_shared_from_this。
class
tcp_connection
:
public
boost::enable_shared_from_this
<
tcp_connection
>
{
public
:
typedef boost::shared_ptr
<
tcp_connection
>
pointer;
static
pointer create(boost::asio::io_service
&
io_service)
{
return
pointer(
new
tcp_connection(io_service));
}
tcp::socket
&
socket()
{
return
socket_;
}
In the function start()
, we call boost::asio::async_write() to serve the data to the client. Note that we are using boost::asio::async_write(), rather than boost::asio::ip::tcp::socket::async_write_some(), to ensure that the entire block of data is sent.
在start()函数中,我们调用boost::asio::async_write()为客户端处理数据。注意:为了确保数据被整块发送,我们使用的是boost::asio::async_write(),而不是boost::asio::ip::tcp::socket::async_write_some()。
void start()
{
The data to be sent is stored in the class member message_
as we need to keep the data valid until the asynchronous operation is complete.
要发送的数据保存在类成员变量message_ 中,在异步操作完成前我们需要保证数据的有效性。
When initiating the asynchronous operation, and if using boost::bind(), you must specify only the arguments that match the handler's parameter list. In this program, both of the argument placeholders (boost::asio::placeholders::error and boost::asio::placeholders::bytes_transferred) could potentially have been removed, since they are not being used in handle_write()
.
当启动一个异步操作时,如果使用boost::bind(),你只需要指定一个符合句柄参数列表签名的参数。在本例中,任何一个参数占位符(boost::asio::placeholders::error 和boost::asio::placeholders::bytes_transferred)皆可被隐式地移除,因为操作write()并没有使用它们。
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind( & tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
Any further actions for this client connection are now the responsibility of handle_write()
.
任何对客户端连接的下一步操作都由handle_write()函数负责处理。
}
private :
tcp_connection(boost::asio::io_service & io_service)
: socket_(io_service)
{
}
void handle_write( const boost::system::error_code & /* error */ ,
size_t /* bytes_transferred */ )
{
}
tcp::socket socket_;
std:: string message_;
};
Removing unused handler parameters
移除无用的操作参数
You may have noticed that the error
, and bytes_transferred
parameters are not used in the body of the handle_write()
function. If parameters are not needed, it is possible to remove them from the function so that it looks like:
你可能已经注意到了:error和bytes_transferred 参数并没有在 handle_write()
函数体内被应用。因此,如果参数并不是必须的,我们可以移除它们,如下所示:
void handle_write()
{
}
The boost::asio::async_write() call used to initiate the call can then be changed to just:
用来发起呼叫的boost::asio::async_write()函数通常可以被改写成下面这样:
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind( & tcp_connection::handle_write, shared_from_this()));
See the full source listing
全部源码:
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt )
//
#include < ctime >
#include < iostream >
#include < string >
#include < boost / bind.hpp >
#include < boost / shared_ptr.hpp >
#include < boost / enable_shared_from_this.hpp >
#include < boost / asio.hpp >
using boost::asio::ip::tcp;
std:: string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time( 0 );
return ctime( & now);
}
class tcp_connection
: public boost::enable_shared_from_this < tcp_connection >
{
public :
typedef boost::shared_ptr < tcp_connection > pointer;
static pointer create(boost::asio::io_service & io_service)
{
return pointer( new tcp_connection(io_service));
}
tcp::socket & socket()
{
return socket_;
}
void start()
{
message_ = make_daytime_string();
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind( & tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private :
tcp_connection(boost::asio::io_service & io_service)
: socket_(io_service)
{
}
void handle_write( const boost::system::error_code & /* error */ ,
size_t /* bytes_transferred */ )
{
}
tcp::socket socket_;
std:: string message_;
};
class tcp_server
{
public :
tcp_server(boost::asio::io_service & io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 13 ))
{
start_accept();
}
private :
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.io_service());
acceptor_.async_accept(new_connection -> socket(),
boost::bind( & tcp_server::handle_accept, this , new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code & error)
{
if ( ! error)
{
new_connection -> start();
start_accept();
}
}
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception & e)
{
std::cerr << e.what() << std::endl;
}
return 0 ;
}