Stay ActiveIf you need to do the following:Io_service Service;Ip::tcp::socket Sock (service);Char buff[512];...read (sock, buffer (buff));in this example, the sock and buff must have a longer time than the read () call. That is, they must be valid before the call to read () is returned. This is what you expect; all arguments you pass to a method must be valid inside the parameter. When we take an asynchronous approach, things get more complicated. Io_service Service;Ip::tcp::socket Sock (service);Char buff[512];void On_read (const boost::system::error_code &, size_t) {}...async_read (sock, buffer (buff), on_read);in this case, both the sock and the buff must exist longer than the read () operation itself, but the time of the read operation is not known because it is asynchronous. when using the socket buffer, you will have a buffer instance that persists at asynchronous invocation (using boost::shared_array<>). Here, we can use the same way, by creating a class and managing the socket and its read-write buffer within it. Then, for all the asynchronous operations, I pass a boost::bind functor that contains the smart pointer:
using namespace Boost::asio; Io_service Service; struct Connection:boost::enable_shared_from_this<connection> {
typedef boost::system::error_code Error_code; typedef boost::shared_ptr<connection> PTR; Connection (): Sock_ (Service), Started_ (True) {} void Start (Ip::tcp::endpoint EP) {
Sock_.async_connect (EP, Boost::bind (&connection::on_connect, Shared_from_this (),
_1)); }
void Stop () { if (!started_) return; Started_ = false; Sock_.close ();
}
BOOL Started () {return started_;} Private
void On_connect (const Error_code & err) {//Here's decide what does with the connection:read or
Write if (!err) do_read ();
else stop (); }
void On_read (const Error_code & err, size_t bytes) { if (!started ()) return; std::string msg (read_buffer_, bytes);
if (msg = = "Can_login") Else if (Msg.find ("data") = = 0) Else if (msg = = "Login_fail")
Do_write ("Access_data");p rocess_data (msg); stop ();
}void on_write (const Error_code & err, size_t bytes) {
Do_read (); }
void Do_read () {
Sock_.async_read_some (Buffer (read_buffer_), boost::bind (&connection::on_read, Shared_from_this (),
_1, _2)); }
void Do_write (const std::string & msg) { if (!started ()) return; Note:in case want to send several messages before // doing another async_read, you'll need several write
Buffers! Std::copy (Msg.begin (), Msg.end (), write_buffer_); Sock_.async_write_some (Buffer (Write_buffer_, msg.size ()),
Boost::bind (&connection::on_write, Shared_from_this (),
_1, _2)); }
void Process_data (const std::string & msg) { //process what comes from server, and then perform another
Write}
Private: ip::tcp::socket sock_; enum {max_msg = 1024x768}; Char read_buffer_[max_msg]; Char write_buffer_[max_msg]; BOOL Started_;
};
int main (int argc, char* argv[]) { ip::tcp::endpoint EP (Ip::address::from_string ("127.0.0.1"),
8001); Connection::p TR (New Connection)->start (EP);
}
In all asynchronous invocations, we pass a boost::bind functor as a parameter. The inside of this functor contains a smart pointer to the connection instance. As long as there is an asynchronous operation waiting, Boost.asio saves a copy of the boost::bind copy, which holds a smart pointer to the connection instance, ensuring that the connection instance remains active. Problem Solving!
Of course, the connection class is just a skeleton class; You need to adjust it to your needs (it looks quite different from the server).
You need to be aware that creating a new connection is fairly straightforward: Connection::p TR (New Connection)->start (EP). This method initiates a (asynchronous) connection to the server. When you need to close this connection, call Stop ().
When the instance is started (Start ()), it waits to be connected. When the connection occurs. On_connect () is called. If no error occurs, it initiates a read operation (Do_read ()). When the read operation is finished, you parse the message; Your application's On_read () will look different. When you write back a message, you need to copy it to the buffer and send it as I did in the Do_write () method, because again, this buffer needs to survive this asynchronous write operation. The last thing to note--when you write back, you need to specify the number of writes, otherwise the entire buffer will be sent out.
Summarize
The Web API is actually much larger, and this chapter is just a reference, and you need to come back to see it when you implement your own Web application.
Boost.asio implements the concept of endpoints, which you can think of as IP and port. If you do not know the exact IP, you can use the resolver object to convert the host name, such as www.yahoo.com, to one or more IP addresses.
We can also see the core--socket class of the API. The Boost.asio provides implementations of TCP, UDP, and ICMP. But you can use your own protocol to extend it, of course, this job is not suitable for timid people.
Asynchronous programming is a necessary evil. You will understand why it is sometimes needed, especially when writing to the server. Calling Service.run () to implement an asynchronous loop can already make you happy, but sometimes you need to go further and try to use Run_one (), poll (), or Poll_one ().
When implementing Async, you can do it asynchronously with your own method, using Service.post () or Service.dispatch ().
Finally, in order for the sockets and buffers (read or write) to be active throughout the lifetime of the asynchronous operation, we need to take special precautions. Your connection class needs to inherit from Enabled_shared_from_this, save the buffer it needs internally, and each asynchronous call passes a smart pointer to the this operation.
The next chapter will give you a real-time operation, and there will be a lot of hands-on programming when implementing ECHO Client/server applications.
Boost.asio C + + Network programming Translator (14)