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.
Related
I am writing my own Aerospike client in C ++ and I have a problem: although my request seems to reach the server (if you send nonsense, the connection will be dropped), the server does not return any response.
Here is my code:
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <iostream>
void read_message(boost::asio::ip::tcp::socket& socket)
{
for (;;)
{
boost::array<char, 1> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break;
else if (error)
throw boost::system::system_error(error);
std::cout.write(buf.data(), len);
std::cout << std::endl;
}
}
void send_message(boost::asio::ip::tcp::socket& socket, std::string message)
{
boost::array<char, 1024> buf;
std::copy(message.begin(), message.end(), buf.begin());
boost::system::error_code error;
socket.write_some(boost::asio::buffer(buf, message.size()), error);
}
int main()
{
std::cout << "Connecting to socket.." << std::endl;
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 3000);
boost::asio::ip::tcp::socket socket(ios);
socket.connect(endpoint);
std::cout << "Connected to socket. Writing message." << std::endl;
send_message(socket, "\x02\x01\x00\x00\x00\x00\x006build\nedition\nnode\nservice\nservices\nstatistics\nversion");
std::cout << "Writed message. Reading response." << std::endl;
read_message(socket);
std::cout << "Read response. Exiting prigram." << std::endl;
socket.close();
return 0;
}
This code works correctly with, for example, 1.1.1.1:80 - HTML is returned with the words "Bad request".
You are calling socket.write_some() only once in your send_message() function. You are basically assuming that all the bytes will be sent in one call. There is no such guarantee. When I tried your code, it sent only 2 bytes in my run. Unless all bytes reach the server, it won't respond (obviously).
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
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.
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'm trying to create a TCP server where the Start() method blocks until a connection is accepted, and then begins a series of asynchronous reads. I have the following code, and when I connect using telnet I get this output:
Waiting for a new connection
Connection accepted
terminate called throwing an exceptionAbort trap: 6
Here is the code:
void SocketReadThread::Start()
{
bzero(m_headerBuffer, HEADER_LEN);
m_running = true;
asio::io_service ios;
asio::ip::tcp::acceptor acp (ios,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), GUI_PORT));
asio::ip::tcp::socket sock(ios);
std::cout << "Waiting for a new connection" << std::endl;
acp.accept(sock);
std::cout << "Connection accepted" << std::endl;
asio::async_read(sock, asio::buffer(m_headerBuffer, HEADER_LEN),
boost::bind(&SocketReadThread::handleReadHeader, shared_from_this(),
asio::placeholders::error));
ios.run();
}
void SocketReadThread::handleReadHeader(const system::error_code& error)
{
std::cout << "Read two bytes!" << std::endl;
}
You should wrap your main() function in try {...} catch (std::exception& e) { cout << e.what(); } block.
You're probably doing something scary (and awesome) to the stack by declaring your ReadHandler incorrectly. Even if you ignore some parameters, the signature must be:
void handler (
const boost::system::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes copied into the
// buffers. If an error occurred,
// this will be the number of
// bytes successfully transferred
// prior to the error.
);