I'm trying to write a simple server using boost/asio on windows, but it throws an error:
Exception thrown at 0x00007FF90EE05B0C in Server.exe:
Microsoft C++ exception:
boost::wrapexcept<boost::asio::ip::bad_address_cast> at memory location 0x0000002A5AF2EFF8. Debug Error!
after I entered the port and IP. I don't understand what could be wrong, because I pass the parameter to the function as written in the implementation of this function.
When I use a constructor with one argument the error does not immediately appear, but when I enter something from the keyboard it appears immediately.
I will be glad to any fair criticism.
Server.h simple header file.
#pragma once
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include "utillity.hpp"
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
using namespace boost::asio;
using socket_ptr = boost::shared_ptr<ip::tcp::socket>;
class MainServer
{
private:
ip::tcp::endpoint ep;
ip::tcp::acceptor acc;
socket_ptr sock;
ip::address ipAddr;
streambuf buffer;
public:
MainServer(boost::asio::io_service& service);
MainServer(boost::asio::io_service& service, const std::string& ip_address, uint16_t port);
~MainServer();
streambuf& getBuffer() { return buffer; }
[[noreturn]] void Handler(boost::asio::io_service& service);
[[noreturn]] void client_session(socket_ptr sock);
[[noreturn]] void Receive_message(socket_ptr sock, streambuf& buffe) const;
[[noreturn]] void send_message(socket_ptr sock, std::string mess) ;
};
namespace util_common
{
std::string create_message()
{
std::string message;
std::getline(std::cin, message);
return message;
}
}
namespace util_server
{
std::pair<std::string, uint16_t> create_ip_and_port()
{
try
{
uint16_t port;
std::string server_ip_address;
std::cout << "IP: ";
std::getline(std::cin, server_ip_address);
std::cout << "Port: ";
std::cin >> port;
std::cin.ignore();
static_cast<uint16_t>(port);
static_cast<std::string>(server_ip_address);
return std::make_pair(server_ip_address, port);
}
catch (const std::exception& e)
{
std::cerr << "Error create_ip_and_port(): " << e.what() << std::endl;
}
}
}
Server.cpp realization
#include "../headers/Server.h"
MainServer::MainServer(boost::asio::io_service& service, const std::string& ip_address, uint16_t port) :
ipAddr (ip::make_address(ip_address)),
ep (ipAddr, port),
acc (service, ep),
sock (new ip::tcp::socket(service))
{
try
{
std::cout << "Server starting in ip: " << ip_address << "and port:" << port << std::endl;
Handler(service);
}
catch (const std::exception& e)
{
std::cerr << "Error constructor MainServer(): " << e.what() << std::endl;
}
}
MainServer::MainServer(boost::asio::io_service& service) :
ipAddr(ip::make_address("0.0.0.0")),
ep(ipAddr, 8080),
acc(service, ep),
sock(new ip::tcp::socket(service))
{
try
{
std::cout << "Server starting on \"0.0.0.0\" ip and port: 8080\n";
Handler(service);
}
catch (const std::exception& e)
{
std::cerr << "Error constructor MainServer(): " << e.what() << std::endl;
}
}
MainServer::~MainServer() = default;
[[noreturn]] void MainServer::Handler(boost::asio::io_service& service)
{
acc.async_accept
(
*sock,
[this, &service](const boost::system::error_code& error)
{
if (!error) _LIKELY
{
std::cout << "Client Connected!" << std::endl;
client_session(sock);
}
else _UNLIKELY
{
std::cout << "Error Handler(): " << error.message() << std::endl;
}
Handler(service);
}
);
service.run();
}
[[noreturn]] void MainServer::client_session(socket_ptr sock)
{
try
{
Receive_message(sock, getBuffer());
send_message(sock, util_common::create_message());
}
catch (const std::exception& e)
{
std::cerr << "Error client_session(socket_ptr sock): " << e.what() << std::endl;
}
}
[[noreturn]] void MainServer::Receive_message(socket_ptr sock, streambuf& buffer) const
{
async_read(*sock, buffer, [this, sock, &buffer](const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (!error) _LIKELY
{
std::istream is(&buffer);
std::string message;
std::getline(is, message);
std::cout << "Client message: " << message << std::endl;
// Consume the data that was read
buffer.consume(bytes_transferred);
// Handle the next message
Receive_message(sock, buffer);
}
else _UNLIKELY
{
std::cout << "Error Receive_message(socket_ptr sock, streambuf& buffer): " << error.message() << std::endl;
}
});
}
[[noreturn]] void MainServer::send_message(socket_ptr sock, std::string mess)
{
async_write
(
*sock,
boost::asio::buffer(mess),
[this, sock, mess](const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (!error) _LIKELY
{
std::cout << "Send message: " << mess << std::endl;
client_session(sock);
}
else _UNLIKELY
{
std::cout << "Error send_message(socket_ptr sock, std::string mess): " << error.message() << std::endl;
}
}
);
}
int main()
{
setlocale(LC_ALL, "Rus");
auto result = util_server::create_ip_and_port();
try
{
io_service service;
MainServer server(service, result.first, result.second);
service.run();
}
catch (const std::exception& e)
{
std::cerr << "Exception in main(): " << e.what() << std::endl;
}
return 0;
}
Full message in console:
'Server.exe' (Win32): Loaded 'D:\C++\Server\out\build\x64-debug\Server.exe'. Symbols loaded.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\ws2_32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\mswsock.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'.
The thread 16212 has exited with code 0 (0x0).
Exception thrown at 0x00007FF90EE05B0C in Server.exe: Microsoft C++ exception: boost::wrapexcept<boost::asio::ip::bad_address_cast> at memory location 0x000000F986B7F298.
Debug Error!
Program: D:\C++\Server\out\build\x64-debug\Server.exe
abort() has been called
(Press Retry to debug the application)
'Server.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\TextShaping.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\uxtheme.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\msctf.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\bcrypt.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\TextInputFramework.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\oleaut32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\CoreMessaging.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\CoreUIComponents.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\cryptbase.dll'.
'Server.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'.
The thread 36256 has exited with code 3 (0x3).
The thread 15152 has exited with code 3 (0x3).
The thread 19884 has exited with code 3 (0x3).
The thread 32800 has exited with code 3 (0x3).
The thread 4196 has exited with code 3 (0x3).
The program '[25276] Server.exe' has exited with code 3 (0x3).
Since the error says that the problem is in the conversion, I tried in every possible way to additionally convert the IP to a string, although I already pass the string as a const parameter.

Your
ipAddris initialized AFTER yourep, which depends on it (in both constructors).This means that
epis never valid. So at least flip the order in which members are declared around e.g.To
Better yet, a quick scan shows that
ipAddris literally never used except to initializeep, so, I'd drop it entirely:Review
There are MANY other things alarming about the code. I'll draw up a quick list reviewing under a coffee soon:
all the
[[noreturn]]attributes are lies. In fact, Asio async initiation function all guarantee immediate return. The operation itself will complete on the service.there is a missing return in
create_id_and_port(which should really have a better name, and just returntcp::endpoint). This invokes UB.your message reading never deals with end-of-file, so it will likely stick in an infinite loop at the end
you are passing a temporary value as a buffer to
send_message. The buffer will be destroyed before the async operation completes, again invoking UB. The buffer needs to be kept alive until the operation completes.send_messagealso needs to guard against overlapping writes. This is unlikely to happen if the input is actually from the console, but with input redirection I wouldn't rely on chance (and Nagle's algorithm) to prevent it.Combine both constructors using default arguments. This reduces code duplication:
You appear to expect line-wise input, yet your
async_readis unbounded. Change it toasync_read_untilwith a delimiter (e.g.'\n'). Then, you don't needistreamandstreambufcomplexxities, instead just use something likestd::stringorstd::deque<char>(the latter being more efficient for consuming from the front end), e.g.:Many functions take
socket_ptrandstreambuf. This is a code smell. It would be more effective to group these methods in their own class (session). This would also allow you to usestd::enable_shared_from_thisto avoid the need forshared_ptrin the first place.Instead of rewriting it all, I'll point to this recent question where I showed the exact same thing (separating concerns between
serverandsession): async_write only sends after the server is closedYou end up doing BLOCKING I/O in client_session:
This blocks the IO service, and refutes the purpose of using Asio.
There's a conceptual problem with accepting many (concurrent) connections. All of them read from
std::cincreating ... a mess at best, and more [UB] if you add multi-threading to the mix.Some aspects of the code seem to "optimize" (e.g. branch-predicting hints). That is contradicted by other signs, such as redundant dynamic allocation.
the useless
static_castexpressions have already been mentionedSIMPLIFY YOUR LIFE
Frankly there end up too many problems in the code which aren't really solvable (without changing the behaviour, like reading from
std::cin), I would consider embrace the non-asynchrony (blocking functions instead of async_XXXX). You can still benefit from all the networking primitives in Asio.In fact you can have extremely simple, very similar, service in a few lines, e.g. using the example from the comments here:
Live On Coliru
With some demos for illustrative purpose:
Of course,
tcp::iostreamdoes not allow Full Duplex IO (Does Boost asio ip tcp iostream support asynch?).You can hack it using some threads very carefully: How to avoid data race with `asio::ip::tcp::iostream`?. However, that answer also says: don't do that. If you need full-duplex, bite the bullet and go async. However, I can only help when you have some requirements that allow this to be demonstrated in the first place.
BONUS: Thread Safe And Async
Here's a best effort implementation of the original "requirements". It uses threads for the console IO and uses mutual exclusion to avoid concurrent access to
std::cin. That's pretty arbitrary, but at least it is safe and allows you to see what techniques I had in mind:Live On Coliru
With a live interactive demonstration: