I have a simple version of a client using boost asio. The client is suppose to receive a response from the server once it sends data. Here is the code of the client
void RunClient()
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query( "127.0.0.1", boost::lexical_cast< std::string >( 7777 )); //9100
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::socket socket(io_service);
socket.async_receive(boost::asio::buffer(buf_client, 3000), 0, ClientReceiveEvent);
boost::asio::connect(socket, endpoint_iterator);
boost::system::error_code ignored_error;
std::cout << "Sending message \n";
boost::asio::write(socket, boost::asio::buffer("Data to send"), ignored_error);
io_service.run();
}
catch (std::exception & ex)
{
std::cout << "[" << boost::this_thread::get_id()<< "] Exception: " << ex.what() << std::endl;
}
}
Here is my ClientReceiveEvent
void ClientReceiveEvent(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(!error)
{
std::cout << "Message: " << buf_client.data() << std::endl;
}
else
{
std::cout << "Error occurred." << error.message() << std::endl;
}
}
I am getting an error from the above method for incoming data
Error occurred.The file handle supplied is not valid
Any suggestion what i am doing wrong in the client ?
Update:
I got the code working however I am confused as to why the statement
socket->async_receive(boost::asio::buffer(buf_client, 3000), 0, ClientReceiveEvent);
needs to be placed after connect. and why the statement
io_service->run();
needs to be placed at the end. I thought this starts the asynch process.
I also wanted to know how would i resend data to the server. I could send it once successfully. How do i resend the command again?
The working code is:
boost::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service);
boost::shared_ptr< boost::asio::ip::tcp::socket > socket( new boost::asio::ip::tcp::socket( *io_service ) );
boost::asio::ip::tcp::resolver resolver(*io_service);
boost::asio::ip::tcp::resolver::query query( "127.0.0.1", boost::lexical_cast< std::string >( 7777 )); //9100
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
socket->connect(endpoint_iterator->endpoint());
socket->async_receive(boost::asio::buffer(buf_client, 3000), 0, ClientReceiveEvent);
boost::system::error_code ignored_error;
std::cout << "Sending message \n";
boost::asio::write(*socket, boost::asio::buffer("some data"), ignored_error);
io_service->run();
Although async_receive schedules a read action that will execute after the code calls run(), it still needs a connected socket at the moment you are calling the async_receive function itself. This is because internally, async_receive is implemented as follows:
this->get_service().async_receive(this->get_implementation(),buffers, 0, BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
The this->get_implementation() expression returns a copy of the internal socket handle as it is at that time, unconnected in your case.
In other words, after the async read has been scheduled, it won't help connecting the socket, because the read will have been scheduled on an unconnected socket.
Related
I am trying to understand what would happen with async_read when there is nothing to read.
For example, a client creates a connection to a server, then start async_read(), but that server does not expect to send anything to this client. So what would happen? Should I receive a EOF?
Updata:
I think #user786653 is right. I made a simple example (see following).
#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
class test{
public:
test(boost::asio::io_service& io_service):_socket(io_service){
}
void handle_connect(){
std::cout<<"entering test::handle_connect"<<std::endl;
char reply[128];
boost::asio::async_read(_socket, boost::asio::buffer(reply, sizeof(reply)),
[](boost::system::error_code ec, std::size_t /*length*/){
std::cout<<"Read result:"<< ec<<" - "<<ec.message()<<std::endl;
});
}
boost::asio::ip::tcp::socket & socket(){
return _socket;
}
private:
boost::asio::ip::tcp::socket _socket;
};
int main() {
try {
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("127.0.0.1", "8000");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
test t(io_service);
t.socket().async_connect(endpoint,boost::bind(&test::handle_connect, &t));
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
Quoting from the latest (1.68.0) documentation:
This function is used to asynchronously read a certain number of bytes of data from a stream. The function call always returns immediately. The asynchronous operation will continue until one of the following conditions is true:
The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes.
An error occurred.
So nothing will happen until the server closes the connection (resulting in an error).
You can test this out for yourself:
#include <iostream>
#include <boost/asio.hpp>
int main() {
try {
boost::asio::io_context io_context;
boost::asio::ip::tcp::socket s(io_context);
boost::asio::ip::tcp::resolver resolver(io_context);
boost::asio::connect(s, resolver.resolve("localhost", "8000"));
char reply[128];
async_read(s, boost::asio::buffer(reply, sizeof(reply)), [](boost::system::error_code ec, std::size_t /*length*/) {
std::cout << "Read result: " << ec << " - " << ec.message() << "\n";
});
io_context.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
Start a server that doesn't respond on localhost port 8000 (or change the code). E.g. something like nc -l 8000 or python -m SimpleHTTPServer. Then run the program and wait. Nothing happens. Now stop the server, on my (Windows) machine this results in:
Read result: system:10054 - An existing connection was forcibly closed by the remote host
If someone could please help me out, I cannot understand how the boost::asio::read function works in boost asio. In boost's example they have it declare the buffer size before the message is received which makes no sense (how do I know how many bytes to read before I read the message?)
I tried this code but it just hangs
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), "localhost", "3000");
tcp::resolver::iterator iterator = resolver.resolve(query);
tcp::socket sock(io_service);
boost::asio::connect(sock, iterator);
cout << "read start" << endl;
boost::system::error_code err_code;
// Read from client.
boost::asio::streambuf read_buffer;
int bytes_transferred = boost::asio::read(sock, read_buffer, err_code);
std::cout << "Read: " << make_string(read_buffer) << std::endl;
read_buffer.consume(bytes_transferred); // Remove data that was read.
Usually, you know how much bytes you want to read from the definition of protocol.
In the opposite case you have to read bytes one by one and observe the error code to stop at the end of connection, file, etc.
Synchronous methods (for example read) in Boost.Asio are nice for short examples, but in practical use cases you should prefer the asynchronous version async_read, which allows you to cancel or just wait for a next chunk of data without blocking your program.
I've encountered the same problem. It seems that boost::asio::read is supposed to be hang there until the connected client is closed. Instead, you can use socket.read_some like this:
using boost::asio::ip::tcp;
try {
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 9999));
for (;;) {
tcp::socket socket(io_service);
acceptor.accept(socket);
std::string message = "server received!\n";
boost::system::error_code error_code;
boost::asio::streambuf stream_buf;
std::vector<char> buf(1024);
size_t len = socket.read_some(boost::asio::buffer(buf), error_code);
std::string received_filename(buf.begin(), buf.end());
received_filename.resize(len);
if (error_code) {
std::cout << "error status: " << error_code.message() << std::endl;
}
boost::asio::write(socket, boost::asio::buffer(message), error_code);
if (error_code) {
std::cout << "error status: " << error_code.message() << std::endl;
}
query_database(tree, received_filename, output_folder, db_image_filenames);
}
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
This will get the message from the client instantly with a relative short message. Hope this help.
The code bellow is mainly the HTTP client example with very few changes to support a download deadline.
It works as expected, but in rare cases e.g. if the internet is unstable, it doesn't work and the deadline can be more than what I set (20 or more seconds when I set 10). This happens very rarely and I am unable to reproduce this, it happens when I don't expect it.
To avoid posting a ton of lines (because few will read them) here is the place where I believe the error lies:
deadline_.expires_from_now(boost::posix_time::milliseconds(deadline));
tcp::resolver::query query(server, "http");
resolver_.async_resolve(query,
boost::bind(&client::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
deadline_.async_wait(boost::bind(&client::check_deadline, this));
Is the order of those lines correct?
And here is the check deadline function:
void check_deadline()
{
if(deadline_cancelled)
return;
else if (deadline_.expires_at() <= deadline_timer::traits_type::now())
socket_.close();
else
deadline_.async_wait(boost::bind(&client::check_deadline, this));
}
You should async_wait() on the deadline timer too. If you don't, you won't get notified, you just check (after the fact) whether the time had expired.
Then if it completes (with an ec other than operation_aborted) then you should
cancel() the async operations on the socket
optionally close the socket
PS. Mmm. It /seems/ that you are doing something similar, although it's unclear where
deadline_cancelled comes from
why you don't accept the error_code in the completion handler for deadline_.async_await and
why you are juggling with time comparisons manually, instead of trusting that the completion handler means what it says
Update Here's a full example doing a HTTP request. In fact, it downloads a million digits of PI from http://www.angio.net/pi/digits.html. This takes a while.
At the start of receiving the response I set a deadline timer for 800ms (and so the transfer should be - correctly - aborted).
This works as advertised. Pay special attention to the canceling of the socket and timer. Note that you could call expires_from_now() again after receiving each chunk of data. This is likely what you want. It will implicitly cancel() the timer each time it hadn't yet expired, so be prepared to handle the operatorion_aborted messages.
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
class client
{
public:
client(boost::asio::io_service& io_service,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
: deadline_(io_service),
socket_(io_service)
{
boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Enter message: ";
static char const raw[] = "GET /pi/digits/pi1000000.txt HTTP/1.1\r\nHost: www.angio.net\r\nConnection: close\r\n\r\n";
static_assert(sizeof(raw)<=sizeof(request_), "too large");
size_t request_length = strlen(raw);
std::copy(raw, raw+request_length, request_);
boost::asio::async_write(socket_,
boost::asio::buffer(request_, request_length),
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
}
}
void deadline_expiration(const boost::system::error_code& error)
{
if (error == boost::asio::error::operation_aborted)
return;
std::cout << "\nDEADLINE REACHED\n";
socket_.cancel();
}
void handle_write(const boost::system::error_code& error,
size_t /*bytes_transferred*/)
{
if (!error)
{
std::cout << "starting read loop\n";
deadline_.expires_from_now(boost::posix_time::millisec(800));
//deadline_.expires_from_now(boost::posix_time::seconds(800));
deadline_.async_wait(boost::bind(&client::deadline_expiration, this, boost::asio::placeholders::error));
boost::asio::async_read_until(socket_,
//boost::asio::buffer(reply_, sizeof(reply_)),
reply_, '\n',
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
}
}
void handle_read(const boost::system::error_code& error, size_t /*bytes_transferred*/)
{
if (!error)
{
std::cout << "Reply: " << &reply_ << "\n";
boost::asio::async_read_until(socket_,
//boost::asio::buffer(reply_, sizeof(reply_)),
reply_, '\n',
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
deadline_.cancel(); // no need for after transfer completed
}
}
private:
boost::asio::deadline_timer deadline_;
boost::asio::ip::tcp::socket socket_;
char request_[1024];
boost::asio::streambuf reply_;
};
int main()
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("www.angio.net", "80");
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
client c(io_service, iterator);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
Coliru link (Coliru doesn't support internet connectivity)
i have a problems with ASIO boost library. The problem are asynchronous functions.
This is simple server code.
void handle_accept( const boost::system::error_code& error )
{
std::cout << "Someone connected" << std::endl;
}
void handle_read( const boost::system::error_code& error )
{
printf( "Message: %s \n", somedata);
}
int main()
{
std::cout << "Starting server ....\n" << std::endl;
try
{
boost::asio::io_service io_service;
tcp::socket mysocket(io_service);
tcp::acceptor myacceptor(io_service, tcp::endpoint(tcp::v4(), 5000));
myacceptor.async_accept( mysocket, boost::bind( &handle_accept, boost::asio::placeholders::error));
mysocket.async_receive( boost::asio::buffer(somedata, 1024) , boost::bind( &handle_read, boost::asio::placeholders::error) );
io_service.run();
std::cout << "END. \n" << std::endl;
sleep(5);
} catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
and client code here
int main()
{
std::cout << "Starting client ....\n" << std::endl;
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), "localhost", "5000");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket mysocket(io_service);
boost::asio::connect(mysocket, endpoint_iterator);
sleep(5);
sprintf( somedata, "This is a message i sent.");
mysocket.send( boost::asio::buffer(somedata, 1024) );
} catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
So, How it should work. Server should wait on connection from client, when client is connected, it should call handle_accept. When client is connected, handle_accept is really called, but it also calls handle_read even no data is recieved !!! why ? client sends data after 5 seconds after making connection.
thank you very much.
The receive completes immediately because it cannot wait because it has nothing to wait for. No data will ever be received on a TCP listening socket. Call async_receive on your connected sockets only.
I have this code. How can I bind my method resolve_handler with the expected iterator and error parameters? Is it the correct way to break down the connection logic?
void FileClient::start()
{
try {
boost::asio::ip::tcp::resolver::query query("ip", "port");
resolver_.async_resolve(query, boost::bind(
&FileClient::resolve_handler, this
));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
void FileClient::resolve_handler(const boost::system::error_code &_error,
boost::asio::ip::tcp::resolver::iterator _it)
{
if (!_error)
socket_.async_connect(*_it, boost::bind(
&FileClient::connect_handler, this, boost::asio::placeholders::error
));
else
std::cerr << "resolve_handler error: " << _error << std::endl;
}
There are examples in boost.asio tutorials, for example, from this HTTP async client
tcp::resolver::query query(server, "http");
resolver_.async_resolve(query,
boost::bind(&client::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
...
void handle_resolve(const boost::system::error_code& err,
tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
// Attempt a connection to the first endpoint in the list. Each endpoint
// will be tried until we successfully establish a connection.
tcp::endpoint endpoint = *endpoint_iterator;
socket_.async_connect(endpoint,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
else
{
std::cout << "Error: " << err.message() << "\n";
}
}
(their handle_connect continues to increment endpoint_iterator as necessary)