ReSharper invalidates coroutines from C++ 20 - c++

I'm getting started with ASIO C++ 20, which uses the coroutines. It marks awaitable, co_await and use_awaitable as "Cannot resolve symbol x". I know it's ReSharper because it works fine until ReSharper loads. The code compiled as expected, the problem is the IntelliSense bug that ReSharper causes. How can I fix it?
#include <cstdlib>
#include <deque>
#include <iostream>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <asio/awaitable.hpp>
#include <asio/detached.hpp>
#include <asio/co_spawn.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/read_until.hpp>
#include <asio/redirect_error.hpp>
#include <asio/signal_set.hpp>
#include <asio/steady_timer.hpp>
#include <asio/use_awaitable.hpp>
#include <asio/write.hpp>
using asio::ip::tcp;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::redirect_error;
using asio::use_awaitable;
//----------------------------------------------------------------------
class chat_participant
{
public:
virtual ~chat_participant() = default;
virtual void deliver(const std::string& msg) = 0;
};
typedef std::shared_ptr<chat_participant> chat_participant_ptr;
//----------------------------------------------------------------------
class chat_room
{
public:
void join(chat_participant_ptr participant)
{
participants_.insert(participant);
for (const auto &msg : recent_msgs_)
participant->deliver(msg);
}
void leave(chat_participant_ptr participant)
{
participants_.erase(participant);
}
void deliver(const std::string& msg)
{
recent_msgs_.push_back(msg);
while (recent_msgs_.size() > max_recent_msgs)
recent_msgs_.pop_front();
for (const auto &participant : participants_)
participant->deliver(msg);
}
private:
std::set<chat_participant_ptr> participants_;
enum { max_recent_msgs = 100 };
std::deque<std::string> recent_msgs_;
};
//----------------------------------------------------------------------
class chat_session
: public chat_participant,
public std::enable_shared_from_this<chat_session>
{
public:
chat_session(tcp::socket socket, chat_room& room)
: socket_(std::move(socket)),
timer_(socket_.get_executor()),
room_(room)
{
timer_.expires_at(std::chrono::steady_clock::time_point::max());
}
void start()
{
room_.join(shared_from_this());
co_spawn(socket_.get_executor(),
[self = shared_from_this()]{ return self->reader(); },
detached);
co_spawn(socket_.get_executor(),
[self = shared_from_this()]{ return self->writer(); },
detached);
}
void deliver(const std::string& msg) override
{
write_msgs_.push_back(msg);
timer_.cancel_one();
}
private:
awaitable<void> reader()
{
try
{
for (std::string read_msg;;)
{
std::size_t n = co_await asio::async_read_until(socket_,
asio::dynamic_buffer(read_msg, 1024), "\n", use_awaitable);
room_.deliver(read_msg.substr(0, n));
read_msg.erase(0, n);
}
}
catch (std::exception&)
{
stop();
}
}
awaitable<void> writer()
{
try
{
while (socket_.is_open())
{
if (write_msgs_.empty())
{
asio::error_code ec;
co_await timer_.async_wait(redirect_error(use_awaitable, ec));
}
else
{
co_await asio::async_write(socket_,
asio::buffer(write_msgs_.front()), use_awaitable);
write_msgs_.pop_front();
}
}
}
catch (std::exception&)
{
stop();
}
}
void stop()
{
room_.leave(shared_from_this());
socket_.close();
timer_.cancel();
}
tcp::socket socket_;
asio::steady_timer timer_;
chat_room& room_;
std::deque<std::string> write_msgs_;
};
//----------------------------------------------------------------------
awaitable<void> listener(tcp::acceptor acceptor)
{
chat_room room;
for (;;)
{
std::make_shared<chat_session>(
co_await acceptor.async_accept(use_awaitable),
room
)->start();
}
}
//----------------------------------------------------------------------
int main()
{
try
{
unsigned short port = 666;
asio::io_context io_context(1);
co_spawn(io_context,
listener(tcp::acceptor(io_context, { tcp::v4(), port })),
detached);
asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&](auto, auto) { io_context.stop(); });
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

This is an issue with R++ and the _MSC_VER predefined macro. Boost expects _MSC_VER >= 1928 in order for coroutines to be enabled, R++ defines it to 1920 at the moment in its internal preprocessor. This will be fixed in R++ 2021.2. As a workaround, you can add this snippet before the boost includes:
#ifdef __RESHARPER__
#define _MSC_VER 1928
#endif
Please report other R++ issues to ReSharper support or directly to the issue tracker. Thanks!

Related

Trying to do Serialization with Struct

I am trying to use the examples from https://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/serialization
But, in my case, I would like to send the struct from client to server. I changed the read and write in the respective client and server. However, I am receiving the following error:
error: ‘s11n_example::connection_ptr’ {aka ‘class boost::shared_ptr<s11n_example::connection>’} has no member named ‘async_read’
conn.async_read(packet_, on the server side.
Any suggestions on how to resolve ?
Client:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
#include "connection.hpp" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include "info.hpp"
namespace s11n_example {
class client
{
public:
client(boost::asio::io_service& io_service,
const std::string& host, const std::string& service)
: connection_(io_service)
{
Packet p;
p.a = 3;
p.b = 2;
packet_.push_back(p);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(host, service);
boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
resolver.resolve(query);
boost::asio::async_connect(connection_.socket(), endpoint_iterator,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
void handle_connect(const boost::system::error_code& e)
{
if (!e)
{
connection_.async_write(packet_,
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error));
}
else
{
std::cerr << e.message() << std::endl;
}
}
void handle_write(const boost::system::error_code& e)
{
}
private:
connection connection_;
std::vector<Packet> packet_;
};
} // namespace s11n_example
int main(int argc, char* argv[])
{
try
{
// Check command line arguments.
if (argc != 3)
{
std::cerr << "Usage: client <host> <port>" << std::endl;
return 1;
}
boost::asio::io_service io_service;
s11n_example::client client(io_service, argv[1], argv[2]);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Server:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include "connection.hpp" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include "info.hpp"
namespace s11n_example {
{
public:
server(boost::asio::io_service& io_service, unsigned short port)
: acceptor_(io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
// Create the data to be sent to each client.
// Start an accept operation for a new connection.
connection_ptr new_conn(new connection(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
void handle_accept(const boost::system::error_code& e, connection_ptr conn)
{
if (!e)
{
conn->async_read(packet_,
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error));
}
connection_ptr new_conn(new connection(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
void handle_read(const boost::system::error_code& e, connection_ptr conn)
{
}
private:
/// The acceptor object used to accept incoming socket connections.
boost::asio::ip::tcp::acceptor acceptor_;
/// The data to be sent to each client.
std::vector<Packet> packet_;
};
} // namespace s11n_example
int main(int argc, char* argv[])
{
try
{
// Check command line arguments.
if (argc != 2)
{
std::cerr << "Usage: server <port>" << std::endl;
return 1;
}
unsigned short port = boost::lexical_cast<unsigned short>(argv[1]);
boost::asio::io_service io_service;
s11n_example::server server(io_service, port);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Info
#ifndef Packet_HPP
#define Packet_HPP
namespace s11n_example {
/// Structure to hold information about a single stock.
struct Packet
{
int a;
int b;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
int a;
int b;
}
};
} // namespace s11n_example
#endif

Multi-threaded async GRPC server with multiple completion queues BLOCKS concurrent requests

I took greeter_async_server & greeter_async_client examples and added multi-threading with a completion queue for each thread. Then added a sleep while processing every other request. If I use single completion queue, it works great. However, below code blocks the second request until the first one is processed. What am I missing here?
Server:
#include <memory>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <chrono>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class ServerImpl final {
public:
~ServerImpl() {
server_->Shutdown();
for (const auto& cq : compl_queues_)
cq->Shutdown();
}
void Run() {
std::string server_address("0.0.0.0:50051");
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service_);
for(auto i{0}; i<10; i++)
compl_queues_.emplace_back(builder.AddCompletionQueue());
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
std::vector<std::thread> threads;
for(auto i{0}; i<10; i++)
threads.emplace_back(std::thread(&ServerImpl::HandleRpcs, this, compl_queues_[i].get()));
for(auto i{0}; i<threads.size(); i++)
threads[i].join();
}
private:
class CallData {
public:
CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq)
: service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
Proceed();
}
void Proceed() {
static int counter{1};
if (status_ == CREATE) {
status_ = PROCESS;
service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_,
this);
} else if (status_ == PROCESS) {
new CallData(service_, cq_);
std::string prefix("Hello ");
reply_.set_message(prefix + request_.name());
if (counter++%2==1)
std::this_thread::sleep_for(std::chrono::seconds(10));
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
delete this;
}
}
private:
Greeter::AsyncService* service_;
ServerCompletionQueue* cq_;
ServerContext ctx_;
HelloRequest request_;
HelloReply reply_;
ServerAsyncResponseWriter<HelloReply> responder_;
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
};
void HandleRpcs(grpc::ServerCompletionQueue* cq) {
new CallData(&service_, cq);
void* tag;
bool ok;
while (true) {
GPR_ASSERT(cq->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
}
std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> compl_queues_;
Greeter::AsyncService service_;
std::unique_ptr<Server> server_;
};
int main(int argc, char** argv) {
ServerImpl server;
server.Run();
return 0;
}
Client:
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient {
public:
explicit GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user) {
HelloRequest request;
request.set_name(user);
HelloReply reply;
ClientContext context;
CompletionQueue cq;
Status status;
std::unique_ptr<ClientAsyncResponseReader<HelloReply> > rpc(
stub_->PrepareAsyncSayHello(&context, request, &cq));
rpc->StartCall();
rpc->Finish(&reply, &status, (void*)1);
void* got_tag;
bool ok = false;
GPR_ASSERT(cq.Next(&got_tag, &ok));
GPR_ASSERT(got_tag == (void*)1);
GPR_ASSERT(ok);
if (status.ok()) {
return reply.message();
} else {
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
auto say_hello = [&]() {
GreeterClient greeter(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::cout << "Saying hello..." << std::endl;
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
};
auto thread{std::thread{ say_hello }};
std::this_thread::sleep_for(std::chrono::seconds(1));
say_hello();
if (thread.joinable())
thread.join();
return 0;
}

boost::asio::async_accept the handler is not called

took example from boost
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/example/cpp11/ssl/server.cpp
exactly as it is assembled it started it works.
then took the implementation from the interface to SSL_Server.h, SSL_Server.cpp, Session.h, Session.cpp, main brought it to Serv.cpp
As a result, the handler is not called !!! I do not understand what is the reason.
SSL_Server.h
#ifndef SSL_SERVER
#define SSL_SERVER
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include "Session.h"
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
class SSL_Server
{
public:
SSL_Server(boost::asio::io_context& io_context, unsigned short port);
private:
void do_accept();
std::string get_password() const;
private:
tcp::acceptor acceptor_;
boost::asio::ssl::context context_;
};
#endif
SSL_Server.cpp
#include "Server_SSL.h"
SSL_Server::SSL_Server(boost::asio::io_context& io_context, unsigned short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
context_(boost::asio::ssl::context::sslv23)
{
context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&SSL_Server::get_password, this));
context_.use_certificate_chain_file("server.crt");
context_.use_private_key_file("server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("dh2048.pem");
do_accept();
}
std::string SSL_Server::get_password() const
{
return "test";
}
void SSL_Server::do_accept()
{
acceptor_.async_accept([this](const boost::system::error_code& error, tcp::socket socket)
{
if (!error)
{
std::make_shared<session>(std::move(socket), context_)->start();
}
});
}
Session.h
#ifndef SESSION
#define SESSION
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket, boost::asio::ssl::context& context);
void start();
private:
void do_handshake();
void do_read();
void do_write(std::size_t length);
private:
boost::asio::ssl::stream<tcp::socket> socket_;
char data_[1024];
};
#endif
Session.cpp
#include "Session.h"
session::session(tcp::socket socket, boost::asio::ssl::context& context)
: socket_(std::move(socket), context)
{
}
void session::start()
{
do_handshake();
}
void session::do_handshake()
{
auto self(shared_from_this());
socket_.async_handshake(boost::asio::ssl::stream_base::server,
[this, self](const boost::system::error_code& error)
{
if (!error)
{
do_read();
}
});
}
void session::do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_),
[this, self](const boost::system::error_code& ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
});
}
void session::do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](const boost::system::error_code& ec,
std::size_t /*length*/)
{
if (!ec)
{
do_read();
}
});
}
Serv.cpp
#include "Server_SSL.h"
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
SSL_Server s(io_context, 3333);
io_context.run();
return 0;
}
I see nothing wrong with the code. In fact it works for me. HOWEVER, you need the certificate, key and DH param files present in the working directory.
I don't see a lot of of explicit error handling, so I suppose this is your likely problem:
context_.use_certificate_chain_file("server.crt", ec);
if (ec) throw boost::system::system_error(ec);
context_.use_private_key_file("server.key", boost::asio::ssl::context::pem, ec);
if (ec) throw boost::system::system_error(ec);
context_.use_tmp_dh_file("dh2048.pem", ec);
if (ec) throw boost::system::system_error(ec);
Note
I used the server.pem and dh2048.pem files from the boost examples, so it became
context_.use_certificate_chain_file("server.pem", ec);
if (ec) throw boost::system::system_error(ec);
context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem, ec);
if (ec) throw boost::system::system_error(ec);

Reconnect asio connection when the server is alive

I want to keep an asio connection responsive, that I mean to check the server every second till it become alive and then send and receive. Here is my code
---irunnable.h---
#ifndef IRUNNABLE_H
#define IRUNNABLE_H
#include <condition_variable>
#include <atomic>
#include <thread>
class IRunnable
{
public:
IRunnable(): mThread(nullptr) {
mRunning.store(false);
}
virtual ~IRunnable() {
if (mRunning.load())
Stop();
delete mThread;
}
virtual void Start() {
mRunning.store(true);
mThread = new std::thread(&IRunnable::Run, this);
}
virtual void Stop() {
mRunning.store(false);
if (mThread && mThread->joinable())
mThread->join();
}
virtual bool IsRunning() {return mRunning.load();}
protected:
virtual void Run() = 0;
std::atomic<bool> mRunning;
private:
std::thread *mThread;
};
#endif // IRUNNABLE_H
---mytcpconnection.h---
#ifndef MYTCPCONNECTION_H
#define MYTCPCONNECTION_H
#include <asio/io_service.hpp>
#include <asio/ip/tcp.hpp>
#include "irunnable.h"
class MyTCPConnection : public IRunnable
{
public:
MyTCPConnection(const std::string &ip, const std::string &port);
~MyTCPConnection();
void Stop();
void Write(const std::string &msg);
void Connect();
protected:
void Run();
private:
void readHeader();
private:
std::string mIP;
std::string mPort;
asio::io_service mIOService;
asio::ip::tcp::socket mSocket;
};
#endif // MYTCPCONNECTION_H
---mytcpconnection.cpp
#include "mytcpconnection.h"
#include <iostream>
#include <asio/connect.hpp>
#include <asio/write.hpp>
#include <asio/read.hpp>
MyTCPConnection::MyTCPConnection(const std::string &ip, const std::string &port):
mIP(ip),
mPort(port),
mSocket(mIOService)
{
Connect();
}
MyTCPConnection::~MyTCPConnection()
{
Stop();
}
void MyTCPConnection::Stop()
{
mIOService.stop();
mSocket.close();
IRunnable::Stop();
}
void MyTCPConnection::Write(const std::string &msg)
{
asio::async_write(mSocket,
asio::buffer(msg.c_str(),
msg.length()),
[this, msg](std::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
std::cout << msg << std::endl;
}
else
{
mSocket.close();
}
});
}
void MyTCPConnection::Run()
{
while (mRunning.load()) {
sleep(1);
// Connect();
std::cout << "before run\n";
mIOService.run();
std::cout << "after run\n";
}
}
void MyTCPConnection::Connect()
{
asio::ip::tcp::resolver resolver(mIOService);
asio::async_connect(mSocket, resolver.resolve({mIP, mPort}),
[this](std::error_code ec, asio::ip::tcp::resolver::iterator)
{
if (!ec)
{
std::cout << "readHeader called\n";
readHeader();
}
else {
std::cout << "can not connect\n";
mSocket.close();
mIOService.reset();
std::cout << "after stopping io_service\n";
}
});
}
void MyTCPConnection::readHeader()
{
const int header_length = 16;
std::shared_ptr<char> msg(new char[header_length]{0},
[](char *c) {
delete[] c;
});
asio::async_read(mSocket,
asio::buffer(msg.get(), header_length),
[this, msg](std::error_code ec, std::size_t s/*length*/)
{
if (!ec)
{
std::cout << "Message read " << s << std::endl;
readHeader();
}
else
{
std::cout << "closing socket\n";
mSocket.close();
mIOService.stop();
}
});
}
---main.cpp---
#include <iostream>
#include "mytcpconnection.h"
using namespace std;
int main()
{
MyTCPConnection conn("127.0.0.1", "12345");
conn.Start();
while (true)
{
conn.Write("Hello server");
sleep(1);
}
return 0;
}
---server.go---
package main
import (
"time"
"fmt"
"net"
)
func handle_conn(conn net.Conn) {
b := make([]byte, 1028)
for {
n, err:= conn.Read(b)
if err != nil {
fmt.Println(err.Error())
break
}
fmt.Println(string(b[:n]))
_, _ = conn.Write([]byte("hellohellohello1"))
time.Sleep(time.Second*1)
}
}
func main() {
ln, err := net.Listen("tcp", ":12345")
if err != nil {
fmt.Println(err.Error())
}
fmt.Println("server started")
for {
conn, err := ln.Accept()
fmt.Println("connection from", conn.RemoteAddr())
if err != nil {
fmt.Println(err.Error())
continue
}
go handle_conn(conn)
}
}
In this form, the client connects to the server (when the server is running) and keeps reading and writing. If I move the Connect() method in the mytcpconnection constructor to the Run() function (where it is commented now), the connection is not able to connect to the server.
How can I fix this problem?

How to support mutiple connections in the TCP server via boost asio

I have the following code of the TCP server:
#include <algorithm>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <string>
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_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return _socket;
}
void start()
{
boost::asio::async_read_until(
_socket
, _message
, "\r\n"
, boost::bind(
&tcp_connection::handle_read
, this->shared_from_this()
, boost::asio::placeholders::error
)
);
}
private:
tcp_connection(boost::asio::io_service& io_service)
: _socket(io_service) {}
void handle_read(const boost::system::error_code& error)
{
if (!error)
{
std::cout << &_message << '\n';
}
}
tcp::socket _socket;
boost::asio::streambuf _message;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: _acceptor(io_service, tcp::endpoint(tcp::v4(), 13))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(_acceptor.get_io_service());
_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();
}
tcp::acceptor _acceptor;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << '\n';
}
}
Am I right that this code doesn't support multiple simultaneous connections properly because async_accept doesn't active while we processing the current message? I mean here:
void handle_accept(
tcp_connection::pointer new_connection
, const boost::system::error_code& error
)
{
// We are unable to accept new connections
if (!error)
{
new_connection->start();
}
// before start_accept function call
start_accept();
}
If yes, how can I solve this problem? How can I write a minimalistic asynchronous TCP server via boost asio with multiple connections support?
This code has support for multiple simultaneous connections as the boost::asio calls used are asynchronous. The new_connection->start() returns immediately and start_accept() is called.