I am writing a c++ project to forward clients websocket requests to server and return their message to each other as soon as each one, this code handles multi clients, but for connecting each client to server async_read stays on the first endpoint, therefore next endpoints however are connected but blocked for reading, by the way every thing is asynchronous.
// my server
class websocket_session : public std::enable_shared_from_this<websocket_session>
{
std::string response = "";
std::shared_ptr<websocket::stream<beast::tcp_stream>> ws_;
boost::beast::multi_buffer buffer_;
boost::beast::multi_buffer buffer_client;
std::string meth = "", endpoint = "", message = "", host = "", port = "";
shared_ptr<SocketClient> javaClient;
net::io_context ioc_client;
public:
// Take ownership of the socket
explicit websocket_session(tcp::socket &&socket)
{
ws_ = make_shared<websocket::stream<beast::tcp_stream>>(std::move(socket));
}
// Start the asynchronous accept operation
template <class Body, class Allocator>
void do_accept(http::request<Body, http::basic_fields<Allocator>> req)
{
stringstream temprequest;
temprequest << req.target();
endpoint = temprequest.str();
temprequest.str(std::string());
temprequest << req.method();
meth = temprequest.str();
std::cout << "\n\t websocket_session::" << __FUNCTION__ << "\n";
//Get IP and port in case endpoint is eligible
if (EndPointSelection::getWebsocketHost(endpoint, meth, req.version(), host, port) == false)
{
std::cerr << "\n\t Websocket is NOT Authorized!!\n";
return;
}
// Set suggested timeout settings for the websocket
ws_->set_option(websocket::stream_base::timeout::suggested(beast::role_type::server));
// Set a decorator to change the Server of the handshake
ws_->set_option(websocket::stream_base::decorator(
[](websocket::response_type &res) {
res.set(http::field::server,
std::string(BOOST_BEAST_VERSION_STRING) +
" advanced-server");
}));
// Accept the websocket handshake
ws_->async_accept(req, beast::bind_front_handler(&websocket_session::on_accept, shared_from_this()));
}
private:
void on_accept(beast::error_code ec)
{
if (ec)
fail(ec, "accept");
std::cout << "\n\t websocket_session::" << __FUNCTION__ << " Endpoint: " << endpoint << "\n";
javaClient = std::make_shared<SocketClient>(ioc_client);
javaClient->run(host.c_str(), port.c_str(), endpoint.c_str());
javaClient->setWebsocket(ws_);
ioc_client.run();
do_read();
}
void do_read()
{
std::cout << "\n\t websocket_session::" << __FUNCTION__ << "\n";
// Read message from client and keep into our buffer
ws_->async_read(buffer_, beast::bind_front_handler(&websocket_session::on_read, shared_from_this()));
}
void on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
std::cout << "\n\t websocket_session::" << __FUNCTION__ << "\n";
// This indicates that the websocket_session was closed
if (ec == websocket::error::closed)
return;
if (ec)
fail(ec, "read");
ws_->text(ws_->got_text());
if (ws_->got_text())
{
std::cout << "\n\t Received Message from Client: " << boost::beast::buffers_to_string(buffer_.data()) << "\n";
sleep(1);
}
ioc_client.stop();
//ws_->async_read(buffer_, beast::bind_front_handler(&websocket_session::on_read, shared_from_this()));
}
void on_write(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
std::cout << "\n\t websocket_session::" << __FUNCTION__ << "\n";
if (ec)
fail(ec, "write");
// Clear the buffer
buffer_client.consume(buffer_client.size());
// Do another read
do_read();
}
};
//my client
void SocketClient::run(char const *host, char const *port, char const *endpoint)
{
// Save these for later
host_ = host;
endpoint_ = endpoint;
std::cout << "\n run Endpoint:" << endpoint_ << "\n";
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// Look up the domain name
resolver_.async_resolve(host, port, beast::bind_front_handler(&SocketClient::on_resolve, shared_from_this()));
}
void SocketClient::setWebsocket(std::shared_ptr<websocket::stream<beast::tcp_stream>> ws_cl)
{
ws_client = ws_cl;
};
void SocketClient::getBuffer(boost::beast::multi_buffer &buffer_client)
{
buffer_client = buffer_;
}
void SocketClient::writeBuffer(boost::beast::multi_buffer buffer_client)
{
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
ws_.write(buffer_client.data());
// ws_.async_write(buffer_client.data(), beast::bind_front_handler(&SocketClient::on_write, shared_from_this()));
//ws_.async_read(buffer_, beast::bind_front_handler(&SocketClient::on_read, shared_from_this()));
}
void SocketClient::on_resolve(beast::error_code ec, tcp::resolver::results_type results)
{
if (ec)
fail(ec, "SocketClient::on_resolve Error: ");
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// Set the timeout for the operation
beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(ws_).async_connect(results, beast::bind_front_handler(&SocketClient::on_connect, shared_from_this()));
}
void SocketClient::on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep)
{
if (ec)
fail(ec, "connect");
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
beast::get_lowest_layer(ws_).expires_never();
// Set suggested timeout settings for the websocket
ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client));
// Set a decorator to change the User-Agent of the handshake
ws_.set_option(websocket::stream_base::decorator(
[](websocket::request_type &req) {
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-client-async");
}));
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host_ += ':' + std::to_string(ep.port());
// Perform the websocket handshake
ws_.async_handshake(host_, endpoint_,
beast::bind_front_handler(
&SocketClient::on_handshake,
shared_from_this()));
}
void SocketClient::on_handshake(beast::error_code ec)
{
if (ec)
fail(ec, "SocketClient::on_handshake Error: ");
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
ws_.async_read(buffer_, beast::bind_front_handler(&SocketClient::on_read, shared_from_this()));
}
void SocketClient::on_write(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
buffer_.consume(buffer_.size());
if (ec)
fail(ec, "SocketClient::on_write Error: ");
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// Read a message from server and keep it into our buffer
ws_.async_read(buffer_, beast::bind_front_handler(&SocketClient::on_read, shared_from_this()));
}
void SocketClient::do_read()
{
ws_.async_read(buffer_, beast::bind_front_handler(&SocketClient::on_read, shared_from_this()));
}
void SocketClient::on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
std::cout << "\nHi from SocketClient::" << __FUNCTION__ << "\n";
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
response_.append(boost::beast::buffers_to_string(buffer_.data())) ;
if (ec)
{
fail(ec, "SocketClient::on_read Error: ");
return;
}
if (response_.empty() == false)
{
std::cout << "\nServer: " << response_ << "\t Size: " << buffer_.size() << "\n";
ws_client->write(net::buffer(response_));
response_="";
}
buffer_.consume(buffer_.size());
sleep(1);
if (ws_client->got_text())
{
std::cout << "\n CLIENT Message\n ";
ws_.write(buffer_.data());
}
ws_.async_read(buffer_, beast::bind_front_handler(&SocketClient::on_read, shared_from_this()));
}
void SocketClient::close_connection()
{
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// Close the WebSocket connection
ws_.async_close(websocket::close_code::normal, beast::bind_front_handler(&SocketClient::on_close, shared_from_this()));
};
void SocketClient::on_close(beast::error_code ec)
{
if (ec)
fail(ec, "SocketClient::on_close Error: ");
std::cout << "\nSocketClient::" << __FUNCTION__ << "\n";
// If we get here then the connection is closed gracefully
// The make_printable() function helps print a ConstBufferSequence
std::cout << beast::make_printable(buffer_.data()) << std::endl;
}
It took me a while to understand what your code is doing - as you left some very relevant portions of your code out. And it appears you've got some boiler plate code that is effectively dead code. The bottom line is this:
I think you need to remove the ioc_client from websocket_session. Introducing a layered set of io_context instances is going to block subsequent connections and just introduce other issues.
I suspect you are trying to create your own instance of io_context because you don't have access to the io_context that's in the run state several layers down your call stack. But I think can get a reference to it.
From the original socket passed into websocket_session, invoke socket.get_executor() (or is it socket.get_executor.context() ?) to get the execution context. That will enable you to create your socket in SocketClient. Don't invoke run on it. It's already in the run state. If that doesn't work, find a way to get the original io_context passed up to your websocket_session.
Related
I'm writing an asynchronous server using boost::asio. I changed boost HTTP server example to use only simple protobufs. Now client gets libc++abi.dylib: terminating with uncaught exception of type boost::wrapexcept<boost::system::system_error>: read: End of file error when server's trying to send message using async_write.
I have tried sync and async clients, different types of buffers but the problem is still there. All the things are going well until async_write.
void TConnection::DoRead() {
std::cout << "Reading data" << std::endl;
auto self(shared_from_this());
Socket.async_read_some(boost::asio::buffer(Buffer),
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
if (!ec) {
TRequestParser::ResultType result;
TMessage request;
request.Parse(Buffer.data(), bytes_transferred);
Reply = MasterPtr->HandleMessange(request);
std::cout << "Replying: " << Reply.Content << std::endl;
DoWrite();
} else if (ec != boost::asio::error::operation_aborted) {
ConnectionManager.Stop(shared_from_this());
}
});
}
void TConnection::DoWrite() {
std::cout << "Writing data" << std::endl;
auto self(shared_from_this());
std::cout << "Replying2: " << Reply.Content << std::endl;
Reply.ToStreamBuf(&Streambuf);
std::cout << "Now I'm gonna write" << std::endl;
std::cout << Streambuf.size() << std::endl;
boost::asio::async_write(Socket, Streambuf,
[this, self](boost::system::error_code ec, std::size_t)
{
std::cout << "Oh, handled" << std::endl;
if (!ec)
{
// Initiate graceful connection closure.
boost::system::error_code ignored_ec;
Socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,
ignored_ec);
}
if (ec != boost::asio::error::operation_aborted)
{
ConnectionManager.Stop(shared_from_this());
}
});
}
What I'm doing wrong?
So I am trying to make a telnet client that connects to some address part for work and part for Boost::Asio learning purpose.
My small project has three handlers:
Resolve handler:
void resolverHandler(const boost::system::error_code& ec, ip::tcp::resolver::iterator iter) {
if (ec) {
cout << "Resolved failed with message: " << ec.message() << endl;
}
else {
ip::tcp::endpoint endpoint = *iter;
cout << "Connection to: " << endpoint.address() << ":" << endpoint.port() << endl;
tcpSocket.async_connect(endpoint, connectHandler);
}
}
Connect handler
void connectHandler(const boost::system::error_code& ec) {
if (ec) {
cout << "Connect failed with message: " << ec.message() << endl;
}
else {
cout << "Connection established" << endl;
tcpSocket.async_read_some(buffer(_data), readHandler);
}
}
Read handler:
void readHandler(const boost::system::error_code& ec, size_t amountOfBytes) {
if (ec) {
cout << "Read failed with message: " << ec.message() << endl;
}
else {
cout << amountOfBytes << endl;
cout << _data.data() << endl;
tcpSocket.async_read_some(buffer(_data), readHandler);
}
}
And this is my main function:
io_service ioservice;
ip::tcp::resolver resolver(ioservice);
ip::tcp::socket tcpSocket(ioservice);
array<char, 16> _data;
ip::tcp::resolver::query query("192.168.200.200", "23");
int main() {
resolver.async_resolve(query, resolverHandler);
ioservice.run();
return 0;
}
But I always get garbage like this:
Connection to: 192.168.206.226:23
Connection established
15
² ² ²# ²' ²$
I admit that I am new to telnet, but I am not sure why do I get this response ? Not sure if I need to null terminate the data that I receive before printing it, but even like that I have the same response.
Here is the normal response I should receive - tried with Windows Telnet:
Welcome Message (localhost) (ttyp0)
login:
Apreciate if someone has any ideas on what to do.
I am using Boost Asio and my goal is to send an HTTPS GET request to www.realmofthemadgod.com. I found some code on GitHub claiming to do that which I've included below but first I'll go through some observations:
The code fails with an error sslv3 alert handshake failure.
On command line, the command openssl s_client -connect www.realmofthemadgod.com:443 results in the same error and some other messages about no certificates being available
However, the command openssl s_client -connect www.realmofthemadgod.com:443 -servername www.realmofthemadgod.com does find the correct certificate!
Now, the problem is how do I achieve what the -servername switch does, in code.
What I have so far:
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#pragma comment(lib, "libcryptoMD.lib")
#pragma comment(lib, "libsslMD.lib")
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class client
{
public:
client(boost::asio::io_service& io_service, boost::asio::ssl::context& context, const std::string& server, const std::string& path)
: resolver_(io_service), socket_(io_service, context)
{
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
std::ostream request_stream(&request_);
request_stream << "GET " << path << " HTTP/1.1\r\n";
request_stream << "Host: " << server << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Start an asynchronous resolve to translate the server and service names
// into a list of endpoints.
tcp::resolver::query query(server, "https");
resolver_.async_resolve(query,
boost::bind(&client::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
private:
void handle_resolve(const boost::system::error_code& err,
tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
std::cout << "Resolve OK" << "\n";
socket_.set_verify_mode(boost::asio::ssl::verify_peer);
//socket_.set_verify_mode(boost::asio::ssl::verify_none);
socket_.set_verify_callback(
boost::bind(&client::verify_certificate, this, _1, _2));
boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error resolve: " << err.message() << "\n";
}
}
bool verify_certificate(bool preverified,
boost::asio::ssl::verify_context& ctx)
{
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// In this example we will simply print the certificate's subject name.
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
std::cout << "Verifying " << subject_name << "\n";
return preverified;
}
void handle_connect(const boost::system::error_code& err)
{
if (!err)
{
std::cout << "Connect OK " << "\n";
socket_.async_handshake(boost::asio::ssl::stream_base::client,
boost::bind(&client::handle_handshake, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Connect failed: " << err.message() << "\n";
}
}
void handle_handshake(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Handshake OK " << "\n";
std::cout << "Request: " << "\n";
const char* header = boost::asio::buffer_cast<const char*>(request_.data());
std::cout << header << "\n";
// The handshake was successful. Send the request.
boost::asio::async_write(socket_, request_,
boost::bind(&client::handle_write_request, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
}
}
void handle_write_request(const boost::system::error_code& err)
{
if (!err)
{
// Read the response status line. The response_ streambuf will
// automatically grow to accommodate the entire line. The growth may be
// limited by passing a maximum size to the streambuf constructor.
boost::asio::async_read_until(socket_, response_, "\r\n",
boost::bind(&client::handle_read_status_line, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error write req: " << err.message() << "\n";
}
}
void handle_read_status_line(const boost::system::error_code& err)
{
if (!err)
{
// Check that response is OK.
std::istream response_stream(&response_);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
std::cout << "Invalid response\n";
return;
}
if (status_code != 200)
{
std::cout << "Response returned with status code ";
std::cout << status_code << "\n";
return;
}
std::cout << "Status code: " << status_code << "\n";
// Read the response headers, which are terminated by a blank line.
boost::asio::async_read_until(socket_, response_, "\r\n\r\n",
boost::bind(&client::handle_read_headers, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error: " << err.message() << "\n";
}
}
void handle_read_headers(const boost::system::error_code& err)
{
if (!err)
{
// Process the response headers.
std::istream response_stream(&response_);
std::string header;
while (std::getline(response_stream, header) && header != "\r")
std::cout << header << "\n";
std::cout << "\n";
// Write whatever content we already have to output.
if (response_.size() > 0)
std::cout << &response_;
// Start reading remaining data until EOF.
boost::asio::async_read(socket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&client::handle_read_content, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error: " << err << "\n";
}
}
void handle_read_content(const boost::system::error_code& err)
{
if (!err)
{
// Write all of the data that has been read so far.
std::cout << &response_;
// Continue reading remaining data until EOF.
boost::asio::async_read(socket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&client::handle_read_content, this,
boost::asio::placeholders::error));
}
else if (err != boost::asio::error::eof)
{
std::cout << "Error: " << err << "\n";
}
}
tcp::resolver resolver_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::streambuf request_;
boost::asio::streambuf response_;
};
int main(int argc, char* argv[])
{
try
{
//boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
ctx.set_default_verify_paths();
boost::asio::io_service io_service;
client c(io_service, ctx, "www.realmofthemadgod.com", "/");
io_service.run();
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
I did some digging and learned that the switch -servername enables something called server name indication (SNI) and I found a way to enable it in code.
This is added to the client constructor:
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host))
{
boost::system::error_code ec((int)ERR_get_error(), boost::asio::error::get_ssl_category());
throw boost::system::system_error(ec);
}
The code still doesn't quite work (it waits for an async callback to happen and times out) but this solves the original problem presented in the question.
I am having a problem while creating a client program that sends requests. The request are using keep alive TCP HTTP connections. When a connection is closed(due to timeout or max being hit), I try and start a new connection if none are available, and resend the request. The connect works fine however, when I try and send the write, nothing is sent(according to Wireshark), but my error code for the write was a success. The receiving server does not receive any information either. Here is the main parts of my code:
void request_handler::send_1(std::vector<std::string> *bid_vector, std::string request_path, boost::mutex *bids_mutex)
{
try
{
boost::asio::streambuf request;
std::ostream request_stream(&request);
std::string reply_information;
request_stream << "GET /tests HTTP/1.1\r\n";
request_stream << "Host: 10.1.10.160\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: keep-alive\r\n\r\n";
server1_mutex_.lock();
if(server1_available_map_.size() == 0)
{
server1_mutex_.unlock();
persistent_connection *new_connection = new persistent_connection("10.1.10.160","80");
if(new_connection->send(request, reply_information))
{
server1_mutex_.lock();
server1_available_map_[new_connection->get_id()] = new_connection;
server1_mutex_.unlock();
}
}
else
{
persistent_connection *current_connection = (*(server1_available_map_.begin())).second;
server1_available_map_.erase(current_connection->get_id());
server1_mutex_.unlock();
int retry_counter = 20;
while(!current_connection->query_rtb(request, reply_information) && --retry_counter != 0)
{
delete current_connection;
server1_mutex_.lock();
if(server1_available_map_.size() == 0)
{
server1_mutex_.unlock();
current_connection = new persistent_connection("10.1.10.160","80");
}
else
{
current_connection = (*(server1_available_map_.begin())).second;
server1_available_map_.erase(current_connection->get_id());
server1_mutex_.unlock();
}
}
//Could not connect to 20 connections
if(retry_counter == 0)
{
Log::fatal("Could not connect in 20 tries");
delete current_connection;
return;
}
server1_mutex_.lock();
server1_available_map_[current_connection->get_id()] = current_connection;
server1_mutex_.unlock();
}
bids_mutex->lock();
bid_vector->push_back(reply_information);
bids_mutex->unlock();
}
catch(boost::thread_interrupted& e)
{
std::cout << "before cancel 1" << std::endl;
return;
}
catch(...)
{
std::cout << "blah blah blah" << std::endl;
}
}
And my persistent_connection class
persistent_connection::persistent_connection(std::string ip, std::string port):
io_service_(), socket_(io_service_), host_ip_(ip)
{
boost::uuids::uuid uuid = boost::uuids::random_generator()();
id_ = boost::lexical_cast<std::string>(uuid);
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(host_ip_,port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *iterator;
socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator));
io_service_.run();
}
void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
if(ec)
{
std::cout << "Couldn't connect" << ec << std::endl;
return;
}
else
{
boost::asio::socket_base::keep_alive keep_option(true);
socket_.set_option(keep_option);
std::cout << "Connect handler" << std::endl;
}
}
bool persistent_connection::send(boost::asio::streambuf &request_information, std::string &reply_information)
{
std::cout << "DOING QUERY in " << id_ << std::endl;
boost::system::error_code write_ec, read_ec;
try
{
std::cout << "Before write" << std::endl;
boost::asio::write(socket_, request_information, write_ec);
std::cout << write_ec.message() << std::endl;
}catch(std::exception& e)
{
std::cout << "Write exception: " << e.what() << std::endl;
}
if(write_ec)
{
std::cout <<"Write error: " << write_ec.message() << std::endl;
return false;
}
boost::array<char,8192> buf;
buf.assign(0);
try
{
std::cout << "Before read" << std::endl;
boost::asio::read(socket_, boost::asio::buffer(buf), boost::asio::transfer_at_least(1), read_ec);
std::cout << read_ec.message() << std::endl;
}catch(std::exception& e)
{
std::cout << "Read exception: " << e.what() << std::endl;
}
if(read_ec)
{
std::cout << "Read error: " << read_ec.message() << std::endl;
return false;
}
reply_information = buf.data();
return true;
}
std::string persistent_connection::get_id()
{
return id_;
}
The path for this to happen is if server1_available_map_.size() > 0, and if the while executes, and fails. And then if the size == 0 on the second server1_available_map_.size();
The output for the call is:
DOING QUERY in 69a8f0ab-2a06-45b4-be26-37aea6d93ff2
Before write
Success
Before read
End of file
Read error: End of file
Connect handler
DOING QUERY in 4eacaa96-1040-4878-8bf5-c29b87fa1232
Before write
Success
Before read
Which shows that the first connection gets an end of file(connection closed by server on other end). The second connection connects fine(Connect handler message), and the query is executed in the second connection(different id), and the write is apparently successful, and the program hangs on the read(because there is nothing to read).
Does anyone have any idea why this would be happening? Is there something I seem to be doing wrong?
Thank you
It looks like you are passing the same boost::asio::streambuf to multiple write calls.
boost::asio::write(socket_, request_information, write_ec);
The contents of the buffer are consumed by the first call to boost::asio::write. This effectively empties the buffer so that there is nothing left to send. Pass a const string if you want to use the same buffer for multiple writes.
Could someone help me with folowing questions?
I'm trying to call async_send within the while loop. The data is sent to server correctly but onSend handler is not called at all... If I do not use the while loop all works fine (the data is sent and received, all handlers ae called)
Will my code work correctly if we send some msgs before server's answer on previous msgs?
Here is the TCP client code
class TCPClient
{
public:
static const size_t maxBufLen = 100;
static const size_t MAX_INPUT_SIZE = 10;
TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter);
void close();
private:
boost::asio::io_service& m_IOService;
tcp::socket m_Socket;
char recieveBuffer[maxBufLen];
void promptTxMsgLoop();
void onConnect(const boost::system::error_code& ErrorCode, tcp::resolver::iterator EndPointIter);
void onReceive(const boost::system::error_code& ErrorCode);
void onSend(const boost::system::error_code& ErrorCode);
void doClose();
};
TCPClient::TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter)
: m_IOService(IO_Service), m_Socket(IO_Service)
{
tcp::endpoint EndPoint = *EndPointIter;
recieveBuffer[0] = '\0';
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::onConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
void TCPClient::onConnect(const boost::system::error_code& ErrorCode, tcp::resolver::iterator EndPointIter)
{
if (ErrorCode == 0)
{
this->promptTxMsgLoop();
}
else if (EndPointIter != tcp::resolver::iterator())
{
cout << "m_Socket.close();!" << endl;
m_Socket.close();
tcp::endpoint EndPoint = *EndPointIter;
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::onConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
}
void TCPClient::promptTxMsgLoop()
{
recieveBuffer[0] = '\0';
while (true)
{
cout << "> " ;
string tmp;
cin >> tmp;
cout << "Entered: " << tmp << endl;
tmp += "\0";
if (tmp.length() < MAX_INPUT_SIZE-1)
{
try
{
//lock untill buffer is emty
while (strlen(recieveBuffer) > 1)
{
}
//onSend handler is never is called inside while loop
m_Socket.async_send(boost::asio::buffer(tmp.c_str(),tmp.length()+1),
boost::bind(&TCPClient::onSend, this, boost::asio::placeholders::error));
}
catch(exception &e)
{
cerr << "Cannot add msg to send queue... " << e.what() << endl;
}
}
else
cout << "Error: input string is too long. Max length is " << MAX_INPUT_SIZE-1 << endl;
}
}
void TCPClient::onSend(const boost::system::error_code& ErrorCode)
{
cout << "Msg has been sent..." << endl;
if (strlen(recieveBuffer) > 1)
cout << "ERROR: recieveBuffer in not epmty. Data is overritten!" << endl;
if (!ErrorCode)
{
m_Socket.async_receive(boost::asio::buffer(recieveBuffer, TCPClient::maxBufLen),
boost::bind(&TCPClient::onReceive, this, boost::asio::placeholders::error));
}
else
{
cout << "onSend closing" << endl;
cout << "ERROR! onSend..." << ErrorCode << endl;
doClose();
}
}
void TCPClient::onReceive(const boost::system::error_code& ErrorCode)
{
cout << "Msg has been received..." << endl;
if (ErrorCode == 0)
{
cout << recieveBuffer << endl;
cout << "msg length: " << strlen(recieveBuffer) << endl;
//unlock buffer
recieveBuffer[0] = '\0';
}
else
{
cout << "ERROR! onReceive..." << ErrorCode << endl;
doClose();
}
}
void TCPClient::doClose()
{
m_Socket.close();
}
int main()
{
try
{
boost::asio::io_service IO_Service;
tcp::resolver Resolver(IO_Service);
tcp::resolver::query Query("127.0.0.1", "1");
tcp::resolver::iterator EndPointIterator = Resolver.resolve(Query);
TCPClient Client(IO_Service, EndPointIterator);
boost::thread ClientThread(boost::bind(&boost::asio::io_service::run, &IO_Service));
ClientThread.join();
Client.close();
}
catch (exception& e)
{
cerr << e.what() << endl;
}
cout << "\nClosing";
getch();
}
In 'onConnect' completion-handler you call promptTxMsgLoop that performs an infinite while loop, so you actually never let io_service to continue its work -- thus no completion handlers will be invoked anymore.
Besides, you call async_send multiple times, without waiting for the comletion handler of the previous async_send, which is also incorrect.
Please, see asio documentation to find out correct use patterns.