Reads are not handled until after another client connects. This is baffling me, it seems straightforward enough. It appears as if the async_accept is blocking the async_read of all the sessions.
Relevant snippets -
server::server(boost::asio::io_service& io_service, int port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
boost::shared_ptr<session> s(new session(io_service_));
acceptor_.async_accept(s->socket(),
boost::bind(&server::handle_accept, this, s,
boost::asio::placeholders::error));
}
void server::handle_accept(boost::shared_ptr<session> s,
const boost::system::error_code& error)
{
s->start();
boost::shared_ptr<session> s(new session(io_service_));
acceptor_.async_accept(s->socket(),
boost::bind(&server::handle_accept, this, s,
boost::asio::placeholders::error));
}
void session::start()
{
boost::asio::async_read_until(socket_,buffer_,' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void session::handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "handling read.";
boost::asio::async_read_until(socket_,buffer_,' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Am I making some obvious mistake in this basic implementation?
Related
I took the example code from boost website and modified it to return responses 100k+ bytes long.
message_ = "HTTP/1.1 200 OK\r\nContent-Length: 100000\r\n\r\n";
message_ += std::string(100000, 'X');
When run and I curl the endpoint, it sometimes returns the correct response but more often it fails after received exactly 65536 bytes with curl: (56) Recv failure: Connection reset by peer.
I've tried adding different socket/acceptor flags, closing the socket, shutdown and writing response in small chunks, but all failed to resolve this. I've also checked that the socket is open on the application side after writing the response, and it is. Application does not report any error code and indicates the entire message has been written (checked the bytes_transferred). Any advice on what I'm doing wrong here?
Full listing below
#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_context& io_context)
{
return pointer(new tcp_connection(io_context));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
message_ = "HTTP/1.1 200 OK\r\nContent-Length: 100000\r\n\r\n";
message_ += std::string(100000, 'X');
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
tcp_connection(boost::asio::io_context& io_context)
: socket_(io_context)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
tcp::socket socket_;
std::string message_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_context& io_context)
: io_context_(io_context),
acceptor_(io_context, tcp::endpoint(tcp::v4(), 9000))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(io_context_);
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error)
{
new_connection->start();
}
start_accept();
}
boost::asio::io_context& io_context_;
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_context io_context;
tcp_server server(io_context);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
The reason this was not working is because the boost example is not prepared to serve HTTP traffic properly. I've found the answer to this from another SO question boost::asio fails to close TCP connection cleanly
In essence it's necessary to read the HTTP request (even if it's not useful) before attempting to send a response. Why the example code from my question works for responses up to 65536 bytes remains a mystery to me, but it's probably because of curl implementation specifics.
So my tcp_connection::start() should look like the following:
void start() {
boost::asio::async_read_until(socket_, input_buffer_, "\r\n\r\n",
boost::bind(&tcp_connection::handle_read,
shared_from_this(),
boost::asio::placeholders::error);
}
With input_buffer_ being boost::asio::streambuf instance defined in tcp_connection. Only after that I can call boost::asio::async_write from within the handle_read.
boost::asio::async_read doesn't call the handler and keeps receiving, am trying to receive some buffers that I don't know it's length , but max is 1024
#include "AsyncConnection.h"
AsyncConnection::AsyncConnection(boost::asio::io_service& io_service)
: socket(io_service)
{
}
AsyncConnection::~AsyncConnection()
{
}
AsyncConnection::Pointer AsyncConnection::Create(boost::asio::io_service& io_service){
return Pointer(new AsyncConnection(io_service));
}
boost::asio::ip::tcp::socket& AsyncConnection::GetSocket(){
return socket;
}
void AsyncConnection::BeginReceive(){
boost::asio::async_read(socket, boost::asio::buffer(buffer, 1024),
boost::bind(&AsyncConnection::EndReceive, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void AsyncConnection::EndReceive(const boost::system::error_code& error, std::size_t bytes_transferred){
if (!error){
}
else{
std::cout << error.message() << std::endl;
}
}
it never calls the EndReceive , my guess is it's keeping receiving until the buffer is full?! how to fix that tho!
I'm developing a 3d (first/third person) game and I'm trying to make it multiplayer using TCP sockets. I'm using the boost asio library for this, and I'm in a little over my head. I've played with the tutorials and examples a bit on the boost asio doc page and they compiled/ran/worked just fine, I'm just a little confused as to how everything works.
Right now I'm just trying to make the server accept messages from the client, and print the message after receiving it. When I execute the code below (it compiles/links/runs fine), nothing happens. More specifically, the client appears to successfully send the message, and the server never seems to receive the message.
Client code:
ClientFramework::ClientFramework() :
mResolver(mIOService)
{
}
bool ClientFramework::Initialize()
{
try
{
tcp::resolver::query query("localhost", "daytime");
tcp::resolver::iterator it = mResolver.resolve(query);
tcp::socket socket(mIOService);
boost::asio::connect(socket, it);
std::string s = "hello world";
boost::system::error_code e;
socket.write_some(boost::asio::buffer(s.c_str(), s.size()), e);
if (e)
{
throw boost::system::system_error(e);
}
} catch (std::exception& e)
{
gLog << LOG_ERROR << e.what() << "\n";
}
return true;
}
Server code:
ServerFramework::ServerFramework() :
mAcceptor(mIOService, tcp::endpoint(tcp::v4(), 13))
{
}
bool ServerFramework::Initialize()
{
mIOService.run();
StartAccept();
return true;
}
void ServerFramework::StartAccept()
{
Connection::ptr conn =
Connection::create(mAcceptor.get_io_service());
mAcceptor.async_accept(conn->Socket(),
boost::bind(&ServerFramework::HandleAccept, this, conn,
boost::asio::placeholders::error));
}
void ServerFramework::HandleAccept(Connection::ptr conn,
const boost::system::error_code& error)
{
if (!error)
{
conn->Initialize();
}
StartAccept();
}
Connection::ptr Connection::create(boost::asio::io_service& io_service)
{
return ptr(new Connection(io_service));
}
tcp::socket& Connection::Socket()
{
return mSocket;
}
void Connection::Initialize()
{
boost::asio::async_read(mSocket, boost::asio::buffer(buf, BUFFER_SIZE),
boost::bind(&Connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Connection::Connection(boost::asio::io_service& io_service) :
mSocket(io_service)
{
}
void Connection::handle_read(const boost::system::error_code& e, size_t size)
{
std::string s(buf, size);
gLog << LOG_INFO << s << "\n";
boost::asio::async_read(mSocket, boost::asio::buffer(buf, BUFFER_SIZE),
boost::bind(&Connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
It does not look like your io_service has any work to do when you invoke run().
bool ServerFramework::Initialize()
{
mIOService.run(); // <-- you don't check the return value here
StartAccept();
return true;
}
it will return the number of handlers executed, I suspect it is zero. Try invoking it after async_accept()
bool ServerFramework::Initialize()
{
StartAccept();
mIOService.run();
return true;
}
Though, it isn't entirely obvious by your limited code snippets where you invoke ServerFramework::Initialize(). I suggest editing your question with a short, self contained, correct example that we can compile with little to no effort. Your current code will not compile without additional boilerplate stuff, like main().
I'm trying to implement two-way asynchronous communication in C++. I'd like to be able to specify the IP address and port number on two machines and be able to get the machines to communicate with each other.
I've looked at Boost::asio and have implemented the following so far:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session{
public:
session(boost::asio::io_service& io_service) : socket_(io_service){
}
tcp::socket& socket(){
return socket_;
}
void start(){
socket_.async_read_some(boost::asio::buffer(data_, max_length),boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
onConnect();
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred){
if (!error){
char* buf = boost::asio::buffer_cast<char*>(boost::asio::buffer(data_, bytes_transferred));
char buf2[bytes_transferred];
int n;
n=sprintf(buf2,"%.*s",bytes_transferred,buf);
onData(buf2);
boost::asio::async_write(socket_, boost::asio::buffer("\0",0), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
}else{
delete this;
}
}
void handle_write(const boost::system::error_code& error){
if (!error){
socket_.async_read_some(boost::asio::buffer(data_, max_length),boost::bind(&session::handle_read, this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}else{
delete this;
}
}
void onConnect(){
printf("Connected\n");
}
void onData(char* buf){
printf("%s",buf);
}
void write(const char* data){
//boost::asio::async_write(socket_, boost::asio::buffer(data, strlen(data)), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
}
private:
tcp::socket socket_;
enum { max_length = 1500 };
char data_[max_length];
};
class server{
public:
server(boost::asio::io_service& io_service, short port) : io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)){
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}
void handle_accept(session* new_session, const boost::system::error_code& error){
if (!error){
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}else{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[]){
try{
if (argc != 2){
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}catch (std::exception& e){
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
I can telnet into this server and send messages to it, but how to access this server programatically from a remote machine? I don't seem to be able to specify an ip address from this code!
I hope someone might have some pointers.
I've not used Boost.ASIO, but searching for "boost asio ip address" and "boost asio gethostbyname" yielded this stuff:
http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/reference/ip__address.html
http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__tcp/resolver.html
The resolver has a resolve method that lets you do things like:
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
boost::asio::ip::tcp::resolver resolver( *io_service );
boost::asio::ip::tcp::resolver::query query(
"www.google.com", // host string
boost::lexical_cast< std::string >( 80 ) // port #
);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query );
boost::asio::ip::tcp::endpoint endpoint = *iterator;
So that'll get you to having a boost::asio::tcp::endpoint which you can use in your socket connection of your client code. The site where I grabbed this is here if you want more details:
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=8
There are plenty of good examples on the Boost.Asio pages to guide you in the right direction.
Simply put, you need to write a server and a client. The former will create the endpoint to connect to and the latter connects. Take a look at the examples. They're very straight-forward and easy to adapt to what your needs.
I'm trying to modify the echo server example from boost asio and I'm running into problem when I try to use boost::asio::async_read_until. Here's the code:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(socket_, boost::asio::buffer(data_, max_length), ' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
std::cout<<"handling read"<<std::endl;
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
/*
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
*/ }
else
{
delete this;
}
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
The problem is when I try to compile I get this weird error:
server.cpp: In member function ‘void session::start()’:
server.cpp:27: error: no matching function for call to ‘async_read_until(boost::asio::basic_stream_socket >&, boost::asio::mutable_buffers_1, char, boost::_bi::bind_t, boost::_bi::list3, boost::arg<1> ()(), boost::arg<2> ()()> >)’
Can someone please explain what's going on? From what I can tell the arguments to async_read_until are correct.
Thanks!
The second argument of async_read_until should be a streambuf object into which the data will be read. To put it simple, you need to pass a boost::asio::streambuf by reference, not a boost::asio::buffer by value.
There is no need to use streambufs. There are overloads that accept dynamic buffers. Dynamic is key since read_until implies the need for a buffer that can increase its size at the callee's discretion. Therefore, all you need is to replace the call to boost::asio::buffer with boost::asio::dynamic_buffer and call it on either std::string or std::vector<char>.
...
void start()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(
socket_, boost::asio::dynamic_buffer(data_), ' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
...
private:
tcp::socket socket_;
std::string data_;
// or
//std::vector<char> data_;
};
I would also get rid of boost::bind since it's an overkill and use a lambda (use them as much as you can)
[this](const boost::system::error_code& error, size_t bytes_transferred) {
handle_read(error, bytes_transferred);
}
const (in)correctness may also cause this type of error message.