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.
Related
I'm trying to learn how Boost.asio works. I've written a basic server and client example as such:
server.cpp
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#define PORT 27015
using namespace boost::asio;
using ip::tcp;
std::string read(tcp::socket& socket) {
boost::system::error_code error;
boost::asio::streambuf buffer;
boost::asio::read(socket, buffer, boost::asio::transfer_all(), error);
if (error) {
std::cerr << "read error: " << error.message() << "\n";
return "ERROR";
}
else {
std::string data = boost::asio::buffer_cast<const char*>(buffer.data());
return data;
}
}
void send(tcp::socket& socket, const std::string& message) {
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(message), error);
if (error)
std::cerr << "send error: " << error.message() << "\n";
else
std::cout << "sent \"" << message << "\" to the client" << "\n";
}
int main() {
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), PORT)); // create listener for new connection(s)
tcp::socket socket(io_service); // socket creation
std::cout << "awaiting connection..." << "\n";
acceptor.accept(socket); // direct connection(s) to the socket we created
std::cout << "accepted connection!" << "\n";
std::string received = read(socket); // receive data
std::cout << "received message: " << received << "\n";
send(socket, "hello from server!"); // send data
}
client.cpp
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#define PORT 27015
using namespace boost::asio;
using ip::tcp;
int main(int argc, char *argv[])
{
boost::asio::io_service io_service;
tcp::socket socket(io_service); // socket creation
std::string server_ipv4_address = "192.168.1.2";
std::cout << "connecting to server at " << server_ipv4_address << "\n";
try {
socket.connect(tcp::endpoint(boost::asio::ip::address::from_string(server_ipv4_address), PORT)); // connection
std::cout << "connected!" << "\n";
}
catch (const boost::system::system_error& e) {
std::cerr << "error while connecting: " << e.what() << "\n";
return -1;
}
boost::system::error_code error; // error holder
std::string message = "hello from client!!\n";
boost::asio::write(socket, boost::asio::buffer(message), error); // send message to server
if (error)
std::cerr << "send failed: " << error.message() << "\n";
else
std::cout << "sent \"" << message << "\" to the server" << "\n";
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error); // receive from server
if (error && error != boost::asio::error::eof)
std::cerr << "receive failed: " << error.message() << "\n";
else {
std::string data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
std::cout << "received data: " << data << "\n";
}
}
The connection gets established properly, but the read() function from the server blocks the program as either it does not receive data from the client, or there is a problem with the way I'm calling it. What seems to be the issue with the boost::asio::read() here?
Everything works properly if I swap boost::asio::read with boost::asio::read_until as shown below. Why does the function work properly in the client but not in the server?
std::string read(tcp::socket& socket) {
boost::system::error_code error;
boost::asio::streambuf buffer;
boost::asio::read_until(socket, buffer, "\n");
std::string data = boost::asio::buffer_cast<const char*>(buffer.data());
return data;
}
Read with the completion condition transfer_all means it will just keep reading until the buffer is full or the connection becomes invalid.
The buffer will "never" be full (since it's a DynamicBuffer).
So that leaves the cause that the client never hangs up.
Everything works properly if I swap boost::asio::read with boost::asio::read_until as shown below.
Exactly. Because then you have another reason to stop reading. Mind you, it could still block forever (when a '\n' never arrives).
Why does the function work properly in the client but not in the server?
It doesn't. It appears to because the server, apparently, does shutdown the connection (signalling EOF). [You would notice this because a subsequent read would return error_code boost::asio::error::eof.]
I have some client application written using boost::asio.
I am trying to implement automatic reconnect when connection is lost. When connection can`t establishment
(for example, when the server is unavailable) or connection issues I trying establish connection again. However, on each attempt,
the application opens a new client port. This leads to a gradual usage of the all dynamic ports of the system.
class client
{
public:
client()
: socket_(nullptr)
, service_()
{
tcp::resolver r(service_);
auto iterator = r.resolve(tcp::resolver::query("127.0.0.1", "4572"));
if (iterator == tcp::resolver::iterator())
throw std::runtime_error("resolve");
endpoint_ = iterator->endpoint();
}
void start()
{
start_connect();
service_.run();
}
private:
void start_connect()
{
std::cout << "Trying " << endpoint_ << "...\n";
socket_ = std::make_shared<tcp::socket>(service_);
socket_->async_connect(endpoint_,
boost::bind(&client::handle_connect, this, _1));
}
void handle_connect(const boost::system::error_code& ec)
{
if (ec)
{
std::cout << "Connect error: " << ec.message() << "\n";
start_connect();
}
else
{
std::cout << "Connected to " << endpoint_ << "\n";
start_read();
}
}
void start_read()
{
boost::asio::async_read_until(*socket_, input_buffer_, '\n',
boost::bind(&client::handle_read, this, _1));
}
void handle_read(const boost::system::error_code& ec)
{
if (!ec)
{
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
if (!line.empty())
std::cout << "Received: " << line << "\n";
start_read();
}
else
{
std::cout << "Error on receive: " << ec.message() << "\n";
start_connect();
}
}
private:
std::shared_ptr<tcp::socket> socket_;
boost::asio::io_service service_;
boost::asio::ip::tcp::endpoint endpoint_;
boost::asio::streambuf input_buffer_;
};
Is it possible to occupy only one client port for the entire execution time of the program? Or at least do not change the port for each attempt to connect to an unavailable server.
I think explicit setting the client port is not good way. Tried not to re-create the socket every time but in this case it is impossible to re-establish the connection.
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 am trying to wrap the boost TCP using a new class in c++. Things work like a charm while I call the boost function directly. However I fail to call socket close while the close is wrap in a class function. Please help have a look on the following codes.
class defination:
typedef boost::shared_ptr<tcp::socket> Socket;
class TCPConnector{
public :
bool isConnected;
Socket sock;
string ip;
int port;
TCPConnector(string ip, int port);
void Close();
bool Connect();
};
functions:
TCPConnector::TCPConnector(string ip,int port):ip(ip),port(port)
{
}
void TCPConnector::Close() {
boost::system::error_code error;
if (!isConnected)
return;
isConnected = false;
try {
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
cout << "ShutDown" << endl;
if (error)
throw boost::system::system_error(error);
sock->close(error);
if (error)
throw boost::system::system_error(error);
} catch (exception& e) {
cout << "#TCPConnector::Close()#" << e.what() << endl;
}
}
Main Function:
int main(int argc, char* argv[]) {
try {
TCPConnector* conn = new TCPConnector("127.0.0.1",8088);
for (int i = 0; i < 2; i++) {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), "127.0.0.1", "8088");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
conn->sock.reset(new tcp::socket(io_service));
conn->sock->connect(*endpoint_iterator);
cout << "Connected" << endl;
boost::thread acceptorThread(boost::bind(receive,conn));
sleep(1);
unsigned char msg[8] = { 0, 6, 55, 56, 55, 56, 55, 0 };
boost::system::error_code error;
try {
boost::asio::write(*conn->sock, boost::asio::buffer(msg, 8),
boost::asio::transfer_all(), error);
cout << "Sent" << endl;
if (error)
throw boost::system::system_error(error);
conn->sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both,
error);
if (error)
throw boost::system::system_error(error);
conn->sock->close(error);//close socket directly , the result is ok
//conn->Close();// close by calling functions, it causes problems.
cout << "Closed" << endl;
if (error)
throw boost::system::system_error(error);
io_service.stop();
} catch (std::exception& e) {
std::cerr << "Exception in thread: " << e.what() << "\n";
}
cout << "Sleep" << endl;
sleep(2);
cout << "Wake up" << endl;
}
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
These 2 lines give the different behaviours. I don't know why the second one will cause problem.
conn->sock->close(error);//close socket directly , the result is ok
conn->Close();// close by calling functions, it causes problems.
mutex: Invalid argument was printed on
sock->close(error);
if (error)
throw boost::system::system_error(error);
Is the problem related to shared_ptr? or I missed something important to close the socket?
Thanks for any suggestion.
The problem is that the io_service should outlive the socket.
On all but the first iteration of the for loop, the statement conn->sock.reset(new tcp::socket(io_service)); calls the destructor of the previous iteration's socket. This destructor accesses elements of the previous iteration's io_service (specifically its mutex) which by that point have themselves been destroyed.
To fix this, you can move the io_service outside the for loop, or you can call conn->sock.reset(); at the end of the for loop in order to invoke the socket's destructor while the io_service is still valid.
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)