mirror of
https://github.com/boostorg/asio.git
synced 2025-05-12 14:11:39 +00:00
499 lines
13 KiB
Plaintext
499 lines
13 KiB
Plaintext
//
|
|
// Copyright (c) 2003-2025 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)
|
|
//
|
|
|
|
/**
|
|
\page tutdaytime1 Daytime.1 - A synchronous TCP daytime client
|
|
|
|
This tutorial program shows how to use asio to implement a client application
|
|
with TCP.
|
|
|
|
\dontinclude daytime1/client.cpp
|
|
\skip #include
|
|
|
|
We start by including the necessary header files.
|
|
|
|
\until asio.hpp
|
|
|
|
The purpose of this application is to access a daytime service,
|
|
so we need the user to specify the server.
|
|
|
|
\until }
|
|
|
|
All programs that use asio need to have at least one I/O execution context,
|
|
such as an boost::asio::io_context object.
|
|
|
|
\until boost::asio::io_context
|
|
|
|
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.
|
|
|
|
\until tcp::resolver
|
|
|
|
A resolver takes a host name and service name and turns them into a list of
|
|
endpoints. We perform a resolve call using the name of the server, specified in
|
|
<tt>argv[1]</tt>, and the name of the service, in this case <tt>"daytime"</tt>.
|
|
|
|
The list of endpoints is returned using an object of type
|
|
boost::asio::ip::tcp::resolver::results_type. This object is a range, with begin() and
|
|
end() member functions that may be used for iterating over the results.
|
|
|
|
\until resolver.resolve
|
|
|
|
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. The boost::asio::connect() function does this for us automatically.
|
|
|
|
\until boost::asio::connect
|
|
|
|
The connection is open. All we need to do now is read the response from the
|
|
daytime service.
|
|
|
|
We use a <tt>std::array</tt> 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 <tt>std::array</tt>, we could have used a <tt>char
|
|
[]</tt> or <tt>std::vector</tt>.
|
|
|
|
\until read_some
|
|
|
|
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.
|
|
|
|
\until }
|
|
|
|
Finally, handle any exceptions that may have been thrown.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
See the \ref tutdaytime1src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Next: \ref tutdaytime2
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime1src Source listing for Daytime.1
|
|
\include daytime1/client.cpp
|
|
Return to \ref tutdaytime1
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime2 Daytime.2 - A synchronous TCP daytime server
|
|
|
|
This tutorial program shows how to use asio to implement a server application
|
|
with TCP.
|
|
|
|
\dontinclude daytime2/server.cpp
|
|
\skip #include
|
|
|
|
\until using
|
|
|
|
We define the function <tt>make_daytime_string()</tt> to create the string to
|
|
be sent back to the client. This function will be reused in all of our daytime
|
|
server applications.
|
|
|
|
\until boost::asio::io_context
|
|
|
|
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.
|
|
|
|
\until tcp::acceptor
|
|
|
|
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.
|
|
|
|
\until acceptor.accept
|
|
|
|
A client is accessing our service. Determine the current time
|
|
and transfer this information to the client.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
Finally, handle any exceptions.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
See the \ref tutdaytime2src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime1 \n
|
|
Next: \ref tutdaytime3
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime2src Source listing for Daytime.2
|
|
\include daytime2/server.cpp
|
|
Return to \ref tutdaytime2
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime3 Daytime.3 - An asynchronous TCP daytime server
|
|
|
|
\section tutdaytime3funcmain The main() function
|
|
|
|
\dontinclude daytime3/server.cpp
|
|
\skip int main()
|
|
\until try
|
|
\until {
|
|
|
|
We need to create a server object to accept incoming client connections. The
|
|
boost::asio::io_context object provides I/O services, such as sockets, that the
|
|
server object will use.
|
|
|
|
\until tcp_server
|
|
|
|
Run the boost::asio::io_context object so that it will perform asynchronous operations
|
|
on your behalf.
|
|
|
|
\until return 0;
|
|
\until }
|
|
|
|
\section tutdaytime3classtcp_server The tcp_server class
|
|
|
|
\dontinclude daytime3/server.cpp
|
|
\skip class tcp_server
|
|
\until public:
|
|
|
|
The constructor initialises an acceptor to listen on TCP port 13.
|
|
|
|
\until private:
|
|
|
|
The function <tt>start_accept()</tt> creates a socket and initiates an
|
|
asynchronous accept operation to wait for a new connection.
|
|
|
|
\until }
|
|
|
|
The function <tt>handle_accept()</tt> is called when the asynchronous accept
|
|
operation initiated by <tt>start_accept()</tt> finishes. It services the client
|
|
request, and then calls <tt>start_accept()</tt> to initiate the next accept
|
|
operation.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
\section tutdaytime3classtcp_connection The tcp_connection class
|
|
|
|
We will use <tt>shared_ptr</tt> and <tt>enable_shared_from_this</tt> because we
|
|
want to keep the <tt>tcp_connection</tt> object alive as long as there is an
|
|
operation that refers to it.
|
|
|
|
\dontinclude daytime3/server.cpp
|
|
\skip class tcp_connection
|
|
\until shared_ptr
|
|
\until }
|
|
\until }
|
|
|
|
In the function <tt>start()</tt>, 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.
|
|
|
|
\until {
|
|
|
|
The data to be sent is stored in the class member <tt>message_</tt> as we need
|
|
to keep the data valid until the asynchronous operation is complete.
|
|
|
|
\until message_
|
|
|
|
When initiating the asynchronous operation, and if using @c std::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 <tt>handle_write()</tt>.
|
|
|
|
\until placeholders::bytes_transferred
|
|
|
|
Any further actions for this client connection are now the responsibility of
|
|
<tt>handle_write()</tt>.
|
|
|
|
\until };
|
|
|
|
\section tutdaytime3remunused Removing unused handler parameters
|
|
|
|
You may have noticed that the <tt>error</tt>, and <tt>bytes_transferred</tt>
|
|
parameters are not used in the body of the <tt>handle_write()</tt> function. If
|
|
parameters are not needed, it is possible to remove them from the function so
|
|
that it looks like:
|
|
|
|
\code
|
|
void handle_write()
|
|
{
|
|
}
|
|
\endcode
|
|
|
|
The boost::asio::async_write() call used to initiate the call can then be changed to
|
|
just:
|
|
|
|
\code
|
|
boost::asio::async_write(socket_, boost::asio::buffer(message_),
|
|
std::bind(&tcp_connection::handle_write, shared_from_this()));
|
|
\endcode
|
|
|
|
See the \ref tutdaytime3src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime2 \n
|
|
Next: \ref tutdaytime4
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime3src Source listing for Daytime.3
|
|
\include daytime3/server.cpp
|
|
Return to \ref tutdaytime3
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime4 Daytime.4 - A synchronous UDP daytime client
|
|
|
|
This tutorial program shows how to use asio to implement a client application
|
|
with UDP.
|
|
|
|
\dontinclude daytime4/client.cpp
|
|
\skip #include
|
|
\until using boost::asio::ip::udp;
|
|
|
|
The start of the application is essentially the same as for the TCP daytime
|
|
client.
|
|
|
|
\until boost::asio::io_context
|
|
|
|
We use an boost::asio::ip::udp::resolver object to find the correct remote endpoint to
|
|
use based on the host and service names. The query is restricted to return only
|
|
IPv4 endpoints by the boost::asio::ip::udp::v4() argument.
|
|
|
|
\until udp::v4
|
|
|
|
The boost::asio::ip::udp::resolver::resolve() function is guaranteed to return at
|
|
least one endpoint in the list if it does not fail. This means it is safe to
|
|
dereference the return value directly.
|
|
|
|
Since UDP is datagram-oriented, we will not be using a stream socket. Create an
|
|
boost::asio::ip::udp::socket and initiate contact with the remote endpoint.
|
|
|
|
\until receiver_endpoint
|
|
|
|
Now we need to be ready to accept whatever the server sends back to us. The
|
|
endpoint on our side that receives the server's response will be initialised by
|
|
boost::asio::ip::udp::socket::receive_from().
|
|
|
|
\until }
|
|
|
|
Finally, handle any exceptions that may have been thrown.
|
|
|
|
\until }
|
|
\until }
|
|
See the \ref tutdaytime4src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime3 \n
|
|
Next: \ref tutdaytime5
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime4src Source listing for Daytime.4
|
|
\include daytime4/client.cpp
|
|
Return to \ref tutdaytime4
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime5 Daytime.5 - A synchronous UDP daytime server
|
|
|
|
This tutorial program shows how to use asio to implement a server application
|
|
with UDP.
|
|
|
|
\dontinclude daytime5/server.cpp
|
|
\skip int main()
|
|
\until boost::asio::io_context
|
|
|
|
Create an boost::asio::ip::udp::socket object to receive requests on UDP port 13.
|
|
|
|
\until udp::socket
|
|
|
|
Wait for a client to initiate contact with us. The remote_endpoint object will
|
|
be populated by boost::asio::ip::udp::socket::receive_from().
|
|
|
|
\until receive_from
|
|
|
|
Determine what we are going to send back to the client.
|
|
|
|
\until std::string message
|
|
|
|
Send the response to the remote_endpoint.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
Finally, handle any exceptions.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
See the \ref tutdaytime5src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime4 \n
|
|
Next: \ref tutdaytime6
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime5src Source listing for Daytime.5
|
|
\include daytime5/server.cpp
|
|
Return to \ref tutdaytime5
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime6 Daytime.6 - An asynchronous UDP daytime server
|
|
|
|
\section tutdaytime6funcmain The main() function
|
|
|
|
\dontinclude daytime6/server.cpp
|
|
\skip int main()
|
|
\until try
|
|
\until {
|
|
|
|
Create a server object to accept incoming client requests, and run
|
|
the boost::asio::io_context object.
|
|
|
|
\until return 0;
|
|
\until }
|
|
|
|
\section tutdaytime6classudp_server The udp_server class
|
|
|
|
\dontinclude daytime6/server.cpp
|
|
\skip class udp_server
|
|
\until public:
|
|
|
|
The constructor initialises a socket to listen on UDP port 13.
|
|
|
|
\until private:
|
|
\until {
|
|
|
|
The function boost::asio::ip::udp::socket::async_receive_from() will cause the
|
|
application to listen in the background for a new request. When such a request
|
|
is received, the boost::asio::io_context object will invoke the
|
|
<tt>handle_receive()</tt> function with two arguments: a value of type
|
|
boost::system::error_code indicating whether the operation succeeded or failed, and a
|
|
<tt>size_t</tt> value <tt>bytes_transferred</tt> specifying the number of bytes
|
|
received.
|
|
|
|
\until }
|
|
|
|
The function <tt>handle_receive()</tt> will service the client request.
|
|
|
|
\until {
|
|
|
|
The <tt>error</tt> parameter contains the result of the asynchronous operation.
|
|
Since we only provide the 1-byte <tt>recv_buffer_</tt> to contain the client's
|
|
request, the boost::asio::io_context object would return an error if the client sent
|
|
anything larger. We can ignore such an error if it comes up.
|
|
|
|
\until {
|
|
|
|
Determine what we are going to send.
|
|
|
|
\until make_daytime_string()
|
|
|
|
We now call boost::asio::ip::udp::socket::async_send_to() to serve the data to the
|
|
client.
|
|
|
|
\until boost::asio::placeholders::bytes_transferred
|
|
|
|
When initiating the asynchronous operation, and if using @c std::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.
|
|
|
|
Start listening for the next client request.
|
|
|
|
\until start_receive
|
|
|
|
Any further actions for this client request are now the responsibility of
|
|
<tt>handle_send()</tt>.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
The function <tt>handle_send()</tt> is invoked after the service request has
|
|
been completed.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
See the \ref tutdaytime6src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime5 \n
|
|
Next: \ref tutdaytime7
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime6src Source listing for Daytime.6
|
|
\include daytime6/server.cpp
|
|
Return to \ref tutdaytime6
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server
|
|
|
|
This tutorial program shows how to combine the two asynchronous servers that we
|
|
have just written, into a single server application.
|
|
|
|
\section tutdaytime7funcmain The main() function
|
|
|
|
\dontinclude daytime7/server.cpp
|
|
\skip int main()
|
|
\until boost::asio::io_context
|
|
|
|
We will begin by creating a server object to accept a TCP client connection.
|
|
|
|
\until tcp_server
|
|
|
|
We also need a server object to accept a UDP client request.
|
|
|
|
\until udp_server
|
|
|
|
We have created two lots of work for the boost::asio::io_context object to do.
|
|
|
|
\until return 0;
|
|
\until }
|
|
|
|
\section tutdaytime7classtcp The tcp_connection and tcp_server classes
|
|
|
|
The following two classes are taken from \ref tutdaytime3 "Daytime.3".
|
|
|
|
\dontinclude daytime7/server.cpp
|
|
\skip class tcp_connection
|
|
\until };
|
|
\until };
|
|
|
|
\section tutdaytime7classudp The udp_server class
|
|
|
|
Similarly, this next class is taken from the
|
|
\ref tutdaytime6 "previous tutorial step".
|
|
|
|
\dontinclude daytime7/server.cpp
|
|
\skip class udp_server
|
|
\until };
|
|
|
|
See the \ref tutdaytime7src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tutdaytime6
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tutdaytime7src Source listing for Daytime.7
|
|
\include daytime7/server.cpp
|
|
Return to \ref tutdaytime7
|
|
*/
|