1

Before I start, I just was trying something out. I don't know yet if I want to do a big project.

I tried making a TCP Socket Server with Boost as it's much easier than winsock. At least, so I thought, but it doesn't work how I want it. What should happen:

  1. Read configuration
  2. Start TCP Socket Server
  3. Run _acceptor.async_accept
  4. Run io_service.run

Now, I got to the point my socket server works and accepts connections. However, I cannot do user input anymore as io_service.run blocks the rest of my server. I must be doing something wrong.

tcp_listener.h:

#pragma once
#include <boost/asio.hpp>
class tcp_listener
{
public:
    tcp_listener(boost::asio::io_service& io_service, std::string ip, short port);
    static void start(tcp_listener* ptr);
    void start_accepting();
private:
    boost::asio::ip::tcp::acceptor _acceptor;
    boost::asio::ip::tcp::socket _socket;
};

tcp_listener.cpp:

#include "tcp_listener.h"
#include "logger.h"
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <memory>

tcp_listener::tcp_listener(boost::asio::io_service& io_service, std::string ip, short port)
    : _acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string(ip), port)),
      _socket(io_service)
{
    logger::log_main("Network bound on %s.%d", _acceptor.local_endpoint().address().to_string().data(), _acceptor.local_endpoint().port());

    start_accepting();

    io_service.run();
}

void tcp_listener::start(tcp_listener* ptr)
{
    ptr->start_accepting();
}

void tcp_listener::start_accepting()
{
    _acceptor.async_accept(_socket, [this](boost::system::error_code ec)
    {
        if (!ec)
        {
            logger::log_main("New connection %s", _socket.remote_endpoint().address().to_string().data());
            //std::make_shared<tcp_client>(std::move(socket_))->start_receiving();
        }
        else
        {
            _acceptor.close();
        }

        start_accepting();
    });
}

engine.h:

#pragma once
#include "configuration.h"
class engine
{
public:
    static void boot();
    static void destroy();
    static configuration* get_config();
private:
    static configuration* config;
};

engine.cpp:

#include "engine.h"
#include "tcp_listener.h"
#include <boost/thread.hpp>
#include <boost/bind.hpp>
configuration* engine::config;
void engine::boot()
{
    engine::config = new configuration("config.cnf");

    boost::asio::io_service io_service;
    tcp_listener& list = tcp_listener(io_service, engine::config->get_value("network.ip"), atoi(engine::config->get_value("network.port").data()));
}

void engine::destroy()
{
    delete engine::config;
}

configuration* engine::get_config()
{
    return engine::config;
}

Main.cpp:

#include "engine.h"
#include <iostream>

int main()
{
    engine::boot();

    for (;;)
    {
        std::string input;
        std::cin >> input;

        if (input == "exit")
        {
            engine::destroy();

            break;
        }
    }

    return 0;
}

I have searched for more than 5 hours, I tried a million things, nothing work. I tried putting it in a thread, resulting me in an exception. Or the socket server itself didn't work.

The user input is useful to reload certain cached data or close the application or something like that.

kenba
  • 4,303
  • 1
  • 23
  • 40
Joshua Bakker
  • 2,288
  • 3
  • 30
  • 63

1 Answers1

3

This is by design.

Just run the service on a separate thread.

 std::thread th([&] { io_service.run(); }); // example

Beware of thread synchronization on shared resources then.

io_service is thread safe (except for special operations like construction, destruction, reset). So, if you must perform tasks that need synchronization it would be easiest to post() it to the service.

As long as you have only 1 thread run-ning the particular io_service instance, you don't need additional synchronization (what is known as a logical or implicit strand¹).

¹ Why do I need strand per connection when using boost::asio?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • If I do that I get a debug error. I have now: `std::thread th([&] { io_service.run(); });` and I tried doing `th.detach()` but it gives the error. Without the detach I also get the error. – Joshua Bakker Nov 24 '16 at 10:24
  • 2
    "the error". Really. The documentation for `std::thread` helps if you are not sure how to use threads in c++11 (which is why I had the `// example` comment, you have to complete it) – sehe Nov 24 '16 at 12:27
  • 2
    Come on. I'm not psychic. Most likely you either start the thread before adding work to the service, or forget to join the thread before the lifetime ends. Or both. You can start a thread in any way you prefer. There are many. Most Boost Asio samples show how to do it anyways. – sehe Nov 24 '16 at 12:28