i made a socket app that uses boost asio how ever it seems to take a lot of cpu when i try to read any thing from the socket.
Atm i use a wrapper class so i dont have to expose the boost header files in my header file that looks something like this:
class SocketHandle
{
public:
SocketHandle()
{
m_pSocket = NULL;
m_pService = NULL;
}
~SocketHandle()
{
delete m_pSocket;
delete m_pService;
}
void connect(const char* host, const char* port)
{
if (m_pSocket || m_pService)
return;
m_pService = new boost::asio::io_service();
tcp::resolver resolver(*m_pService);
tcp::resolver::query query(tcp::v4(), host, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
m_pSocket = new tcp::socket(*m_pService);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
(*m_pSocket).close();
(*m_pSocket).connect(*endpoint_iterator++, error);
}
if (error)
throw ...
}
tcp::socket* operator->()
{
return m_pSocket;
}
private:
tcp::socket *m_pSocket;
boost::asio::io_service *m_pService;
};
and then im reading from the socket like so:
size_t recv(char *data, size_t size)
{
boost::system::error_code error;
size_t len = (*m_pSocket)->read_some(boost::asio::buffer(data, size), error);
if (error)
throw ...
return len;
}
Am i doing something wrong? Is there a better way to read data from the socket?
Boost 1.39.0
visual c++
windows
A change you may wish to consider (and I highly recommend) is to make your socket calls asynchronous. That way you don't have to worry about threads being blocked or any socket calls spinning internally (which I suspect may be what you're seeing). Instead, you simply provide a callback that will receive any errors and the number of bytes received.
There are plenty of examples in the Boost docs that illustrate how to do this, and I've found that it lends itself to a much more efficient use of threads and processor resources.
You should avoid the tight while loop:
// BAD.
while (error && endpoint_iterator != end)
{
(*m_pSocket).close();
(*m_pSocket).connect(*endpoint_iterator++, error);
}
Instead try something like:
try
{
(*m_pSocket).connect(*endpoint_iterator++, error);
// ...
}
catch (std::exception& ex)
{
// Release resources, then try connecting again.
}
Also see these examples at for the right idioms of using Asio.
Consider using the free function instead,
size_t len = asio::read(*m_pSocket,asio::buffer(data, size), error);
Related
i want, to get the url content (http://127.0.0.1:1337/test/test), so in this case "/test/test", how i can do that?
using tcp = boost::asio::ip::tcp;
void ts3plugin_initWebsocket() {
try
{
auto const address = boost::asio::ip::make_address("127.0.0.1");
auto const port = static_cast<unsigned short>(std::atoi("1337"));
boost::asio::io_context ioc{ 1 };
tcp::acceptor acceptor{ ioc, {address, port} };
while (true) {
tcp::socket socket{ ioc };
acceptor.accept(socket);
ts3Functions.logMessage("Connected", LogLevel_INFO, "Plugin", 1);
}
}
catch (const std::exception& e)
{
char msg[512];
snprintf(msg, sizeof(msg), "Error: %s", e.what());
ts3Functions.logMessage(msg, LogLevel_INFO, "Plugin", 1);
}
}
This has little to do with Asio, and everything with HTTP. You want to make a GET request, see e.g. https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET
Going by the naming you might also want to upgrade to Websocket protocol over HTTP. Instead of figuring out how to do all that, perhaps just go by one of the Beast examples:
https://www.boost.org/doc/libs/1_78_0/libs/beast/doc/html/beast/examples.html
We have a C++ application that talks to a server. It sends two messages to it, and the server responds to each message with another message. We're using Boost, but the Boost Socket--the entire application--barfs when we attempt to close the socket.
Here's the general idea of what we're doing:
encode the message (change it into a string)
open socket
send message
check the bytes sent
check the return message
shutdown & close the socket
Since we send two messages, we do it in a loop (just two iterations, obviously).
We know exactly where the error is since, if we remove that line, it works fine. It's on step 5. Unfortunately, that's kind of an important step. We can't find what we're doing wrong how to fix it.
Here's the code:
bool ReallyImportantService::sendMessages( int messageNum ) {
// ...some error-checking here...
bool successCode = false;
for( int i = 0; i < 2; ++i ) {
successCode = false;
unique_ptr<boost::asio::ip::tcp::socket> theSocket = connect();
if( theSocket == nullptr ) {
theLogger->error( "Could not create socket, could not send input messageNum to service" );
return successCode;
}
string message = encodeMessage( messageNum );
// send the message
boost::system::error_code error;
size_t bytesSent = boost::asio::write(*theSocket,
boost::asio::buffer(message),
boost::asio::transfer_all(), error);
// inspect the result
if( !messageNumSendSuccessful(message.length(), bytesSent) ) {
return successCode;
}
// Get the response message
string response;
boost::system::error_code e;
boost::asio::streambuf buffer;
// this is step #5 above, the line that kills it. But it responds with no errors
boost::asio::read_until(*theSocket, buffer, "\0", e);
if( e.value() == boost::system::errc::success ) {
istream str(&buffer);
getline(str, response);
// validate response
successCode = messageAckIsValid( response, messageNum );
}
else {
theLogger->error( "Got erroneous response from server when sending messageNum" );
}
// close it all up
boost::system::error_code eShut;
theSocket->shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, eShut);
// We never get an error code here, all clean
try {
boost::system::error_code ec;
// This is where it all goes belly-up. It doesn't throw an exception, doesn't return an
// error-code. Stepping through, we can see the call stack shows a Segmentation fault,
// but we don't know what could be causing this.
theSocket->close( ec );
}
catch(boost::system::system_error& se) {
theLogger->error( "sendMessages() barfed on close! " + string(se.what()) );
}
catch( ... ) {
theLogger->error( "sendMessages() barfed on close! " );
}
}
return successCode;
}
string ReallyImportantService::encodeMessage( int messageNum ) {
// Encode the message
stringstream ss;
ss << "^FINE=";
ss << to_string(messageNum) << "\n";
string message = ss.str();
theLogger->info( message );
return message;
}
unique_ptr<boost::asio::ip::tcp::socket> ReallyImportantService::connect() {
// Addresses from configuration
string address( server_ip );
string port( server_port );
// Resolve the IP address
boost::asio::io_service ioService;
boost::asio::ip::tcp::resolver resolver(ioService);
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::resolver::iterator ep_iterator = resolver.resolve(query);
// create the socket
unique_ptr<boost::asio::ip::tcp::socket> theSocket = make_unique<boost::asio::ip::tcp::socket>(ioService);
// not sure if this is necessary, but couldn't hurt; we do reuse the IP address the second time around
boost::system::error_code ec;
theSocket->set_option(boost::asio::socket_base::reuse_address(true), ec);
// Connect
try {
boost::asio::connect(*theSocket, ep_iterator);
} catch(const boost::system::system_error &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
} catch(const exception &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
}
return theSocket;
}
Here's the call stack we get when it errors-out:
(Suspended : Signal : SIGSEGV:Segmentation fault)
pthread_mutex_lock() at 0x7ffff7bc8c30
boost::asio::detail::posix_mutex::lock() at posix_mutex.hpp:52 0x969072
boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock() at scoped_lock.hpp:36 0x980b66
boost::asio::detail::epoll_reactor::free_descriptor_state() at epoll_reactor.ipp:517 0x96c6fa
boost::asio::detail::epoll_reactor::deregister_descriptor() at epoll_reactor.ipp:338 0x96bccc
boost::asio::detail::reactive_socket_service_base::close() at reactive_socket_service_base.ipp:103 0xb920aa
boost::asio::stream_socket_service<boost::asio::ip::tcp>::close() at stream_socket_service.hpp:151 0xb975e0
boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >::close() at basic_socket.hpp:339 0xb94f0d
ReallyImportantService::sendMessages() at ReallyImportantService.cc:116 0xb8ce19
<...more frames...>
We created a minimal implementation that just:
Creates the socket
Shuts down the socket
Closes the socket
And it works perfectly. We put it in a loop and we can go for dozens of iterations without any problems.
We're using Eclipse CDT and gcc to compile.
Any idea what might be going on?
You've broken the cardinal rule.
An io_service must outlive all objects created on it.
Your connect() function creates an io_service, creates a socket on it and returns the socket (wrapped in a unique_ptr). Then the io_service is destroyed.
From that point forward, all bets are off because the socket will use a the socket service object associated with the io_service you just destroyed. This socket service is now just memory with undefined values in it. You're (un)lucky the program got this far before the segfault.
In general you will need one io_service per application. All objects that need it should carry a reference to it.
Your connect function then becomes:
bool connect(boost::asio::ip::tcp& theSocket) {
// Addresses from configuration
string address( server_ip );
string port( server_port );
// Resolve the IP address
boost::asio::ip::tcp::resolver resolver(theSocket.get_io_service());
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::resolver::iterator ep_iterator = resolver.resolve(query);
// not sure if this is necessary, but couldn't hurt; we do reuse the IP address the second time around
boost::system::error_code ec;
theSocket.set_option(boost::asio::socket_base::reuse_address(true), ec);
// Connect
try {
boost::asio::connect(theSocket, ep_iterator);
} catch(const boost::system::system_error &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
return false;
} catch(const exception &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
return false;
}
return true;
}
bool sendMessages(boost::asio::io_service& ios, int messageNum)
{
boost::asio::ip::tcp::socket theSocket(ios);
auto ok = connect(theSocket);
// ... carry on ...
}
Prefer to hold references to sockets etc whenever possible. Wrapping them in a unique_ptr is a confusing extra layer of indirection.
As of c++11 and recent versions of boost, asio sockets are moveable. You can return them by value as opposed to passing in a reference as I have done.
I notice that you have a mixture of exception and non-exception error handling in the code. You probably want to stick to one or the other (in my view exception-based error handling is cleaner, but this is not a universal view).
My client-server app, that communicating through boost asio, usign functions:
When connection starts, client send to server bunch of requests, server send back some response.
After adding asio::ssl to project i get following problem.
Sometimes, 1/5 times, server reads only first fixed part of requests. When client disconnected, server get all missed requests.
On client side all seems good, callbakcs called with no errors and writed sizes are proper. But result from packet sniffer show that client not sending this part of requests.
Client :
Size of each "frame" located in header, first must read atleast header.
Thread Worker used for background work, and pushing ready packets to storage.
using SSLSocket = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
class AsyncStrategy :
public NetworkStrategy
{
// other data...
void _WriteHandler(const boost::system::error_code& err, size_t bytes);
bool Connect(const boost::asio::ip::tcp::endpoint& endpoint);
void _BindMessage();
void _BindMessageRemainder(size_t size);
void _AcceptMessage(const boost::system::error_code& err_code, size_t bytes);
void _AcceptMessageRemainder(const boost::system::error_code& err_code, size_t bytes);
// to keep io_service running
void _BindTimer();
void _DumpTimer(const boost::system::error_code& error);
void _SolveProblem(const boost::system::error_code& err_code);
void _Disconnect();
bool verify_certificate(bool preverified,
boost::asio::ssl::verify_context& ctx);
PacketQuery query;
boost::array <Byte, PacketMaxSize> WriteBuff;
boost::array <Byte, PacketMaxSize> ReadBuff;
boost::asio::ip::tcp::endpoint ep;
boost::asio::io_service service;
boost::asio::deadline_timer _Timer{ service };
boost::asio::ssl::context _SSLContext;
SSLSocket sock;
boost::thread Worker;
bool _ThreadWorking;
bool _Connected = false;
};
AsyncStrategy::AsyncStrategy( MessengerAPI& api)
: API{api},_SSLContext{service,boost::asio::ssl::context::sslv23 },
sock{ service,_SSLContext }, _Timer{service},
Worker{ [&]() {
_BindTimer();
service.run();
} },
_ThreadWorking{ true }
{
_SSLContext.set_verify_mode(boost::asio::ssl::verify_peer);
_SSLContext.set_verify_callback(
boost::bind(&AsyncStrategy::verify_certificate, this, _1, _2));
_SSLContext.load_verify_file("ca.pem");
}
bool AsyncStrategy::verify_certificate(bool preverified,
boost::asio::ssl::verify_context& ctx)
{
return preverified;
}
void AsyncStrategy::_BindMessage()
{
boost::asio::async_read(sock, buffer(ReadBuff,BaseHeader::HeaderSize()),
boost::bind(&AsyncStrategy::_AcceptMessage, this, _1, _2));
}
bool AsyncStrategy::Connect(const boost::asio::ip::tcp::endpoint& endpoint)
{
ep = endpoint;
boost::system::error_code err;
sock.lowest_layer().connect(ep, err);
if (err)
throw __ConnectionRefused{};
// need blocking handshake
sock.handshake(boost::asio::ssl::stream_base::client, err);
if (err)
throw __ConnectionRefused{};
_BindMessage();
return true;
}
void AsyncStrategy::_AcceptMessage(const boost::system::error_code& err_code, size_t bytes)
{
// checking header, to see, packet ends or not
// if there is more data in packet, read rest my binding function
// pseudocode
if( need_load_more )
_BindMessageRemainder(BytesToReceive(FrameSize));
return;
}
// if not use this bind this function next time
_CheckPacket(ReadBuff.c_array(), bytes);
_BindMessage();
}
void AsyncStrategy::_AcceptMessageRemainder(const boost::system::error_code& err_code, size_t bytes)
{
if (err_code)
{
_SolveProblem(err_code);
return;
}
_CheckPacket(ReadBuff.c_array(), bytes + BaseHeader::HeaderSize());
_BindMessage();
}
bool AsyncStrategy::Send(const TransferredData& Data)
{
// alreay known, that that data fits in buffer
Data.ToBuffer(WriteBuff.c_array());
boost::asio::async_write(sock,
buffer(WriteBuff, Data.NeededSize()),
boost::bind(&AsyncStrategy::_WriteHandler, this, _1, _2));
return true;
}
void AsyncStrategy::_WriteHandler(const boost::system::error_code& err, size_t bytes)
{
if (err)
_SolveProblem(err);
}
After removing all ssl stuff, data transfer is normal. As i mentioned, all works properly before ssl integration.
Finding solution, i discovered that if send with delay, tried 200 ms, all data transferring normally.
Win10, boost 1.60, OpenSSL 1.0.2n
I guess there may be an error in my code, but I tried almost everything I thought. Looking for advice.
We can't see how Send is actually called.
Perhaps it needs to be synchronized.
We can that it reuses the same buffer each time, so two writes overlapping will clobber that buffer.
We can also see that you're not verifying that the size of the Data argument fits into the PacketMaxSize buffer.
This means you will not only lose data if you exceed the expected buffer size, it will also invoke Undefined Behaviour
I have a server running very heavy 3D simulations that I want to display in real time on a client machine. For now I am running my tests in localhost to get rid of the network brandwidth and latency issues, and I use boost::asio to stream my data (geometry) through the network.
I have to use tcp because I have to compress my geometry, split it into multiple packages and then send it through the network, and on the client, gather the packages to rebuild my archive, so network packages have to arrive in the good order.
This works pretty well, I can run my simulation and stream my data at ~90-120fps, depending on the quantity of data to stream, which is very good.
My problem is that sometimes, it suddenly takes ~1second for the socket to connect() on the client, and consequently as much time for the server's to accept(). This causes my simulation to stop being streamed randomly, and I can't find the problem.
I though the problem could come from some kind of buffer overflow on the socket, preventing the server to write more data as long as the client didn't read some, but it can't be that, since I have no latency between the client and the server, so the client reads the packages fast enough (as soon as they arrive, basically)
Here's a shortened piece of code for the server:
while (1)
{
//archive some data in a stringstream using boost::archive...
boost::asio::io_service ioservice;
tcp::acceptor acceptor(ioservice, tcp::endpoint(tcp::v4(), PORT));
boost::system::error_code ignored_error;
tcp::socket socket(ioservice);
acceptor.accept(socket);
gettimeofday(&m_tv, NULL);
accept += (m_tv.tv_usec - m_timer);
m_timer = m_tv.tv_usec;
size_t bytes_sent = boost::asio::write(socket, boost::asio::buffer(ss.str()), boost::asio::transfer_all(), ignored_error);
}
and on the client I get something like:
while (1)
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(IP, PORT);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if (error)
throw boost::system::system_error(error);
while(1)
{
boost::array<char, 200000> buf;
ss.write(buf.data(), bytes_received);
boost::system::error_code error;
bytes_received = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break;
else if (error)
throw boost::system::system_error(error);
}
}
I create a socket every frame, which is probably the problem, but I couldn't find an easier way of telling my client that he finished reading the package. By closing the socket every frame, I send eof to the client who then knows that he can build the archive using the data retrieved.
Is there something I can do to avoid opening a socket every frame, without having to check the content of my packages to know the size of the data to retrieve?
I have this piece of code using standard sockets:
void set_fds(int sock1, int sock2, fd_set *fds) {
FD_ZERO (fds);
FD_SET (sock1, fds);
FD_SET (sock2, fds);
}
void do_proxy(int client, int conn, char *buffer) {
fd_set readfds;
int result, nfds = max(client, conn)+1;
set_fds(client, conn, &readfds);
while((result = select(nfds, &readfds, 0, 0, 0)) > 0) {
if (FD_ISSET (client, &readfds)) {
int recvd = recv(client, buffer, 256, 0);
if(recvd <= 0)
return;
send_sock(conn, buffer, recvd);
}
if (FD_ISSET (conn, &readfds)) {
int recvd = recv(conn, buffer, 256, 0);
if(recvd <= 0)
return;
send_sock(client, buffer, recvd);
}
set_fds(client, conn, &readfds);
}
I have sockets client and conn and I need to "proxy" traffic between them (this is part of a socks5 server implementation, you may see https://github.com/mfontanini/Programs-Scripts/blob/master/socks5/socks5.cpp). How can I achieve this under asio ?
I must specify that until this point both sockets were operated under blocking mode.
Tried to use this without success:
ProxySession::ProxySession(ba::io_service& ioService, socket_ptr socket, socket_ptr clientSock): ioService_(ioService), socket_(socket), clientSock_(clientSock)
{
}
void ProxySession::Start()
{
socket_->async_read_some(boost::asio::buffer(data_, 1),
boost::bind(&ProxySession::HandleProxyRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void ProxySession::HandleProxyRead(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(*clientSock_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&ProxySession::HandleProxyWrite, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void ProxySession::HandleProxyWrite(const boost::system::error_code& error)
{
if (!error)
{
socket_->async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&ProxySession::HandleProxyRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
The issue is that if I do ba::read(*socket_, ba::buffer(data_,256)) I can read data that comes from my browser client through socks proxy but in the version from above ProxySession::Start does not lead to HandleProxyRead being called in any circumstances.
I don't really need an async way of exchanging data here, it;s just that I've come by with this solution here. Also from where I called ProxySession->start from code I needed to introduce a sleep because otherwise the thread context from which this was executing was being shut down.
*Update 2 * See below one of my updates. The question block is getting too big.
The problem ca be solved by using asynchronous write/read functions in order to have something similar with presented code. Basically use async_read_some()/async_write() - or other async functions in these categories. Also in order for async processing to work one must call boost::asio::io_service.run() that will dispatch completion handler for async processing.
I have managed to come with this. This solution solves the problem of "data exchange" for the 2 sockets (that must happen acording to socks5 server proxy) but it is very compute intensive. Any ideas ?
std::size_t readable = 0;
boost::asio::socket_base::bytes_readable command1(true);
boost::asio::socket_base::bytes_readable command2(true);
try
{
while (1)
{
socket_->io_control(command1);
clientSock_->io_control(command2);
if ((readable = command1.get()) > 0)
{
transf = ba::read(*socket_, ba::buffer(data_,readable));
ba::write(*clientSock_, ba::buffer(data_,transf));
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
}
if ((readable = command2.get()) > 0)
{
transf = ba::read(*clientSock_, ba::buffer(data_,readable));
ba::write(*socket_, ba::buffer(data_,transf));
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
}
}
}
catch (std::exception& ex)
{
std::cerr << "Exception in thread while exchanging: " << ex.what() << "\n";
return;
}