3

Hi I have this code in my thread:

boost::asio::io_service io_service;


tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), serverPort));

r->serverRunning = true;
tcp::socket socket_(io_service);
io_service.run();

while(1)
{
    if(boost::this_thread::interruption_requested())
    {
        acceptor.close();                    

    }
    acceptor.accept(socket_);

    io_service.notify_fork(boost::asio::io_service::fork_prepare);

    if (fork() == 0)
    {
        io_service.notify_fork(boost::asio::io_service::fork_child);
        acceptor.close();

        break;

    }
    else
    {
        io_service.notify_fork(boost::asio::io_service::fork_parent);
        socket_.close();
        continue;

    }
}
sent something to client....

so basically I want to accept a new conection and make a fork... child will be sending something to client and parent will accept next connection...

But when I run this code after connecting a client it will be sending some information just for a short time and then it will stop sending informations... any ideas why?

Thanks

EDIT: After the while I`m trying to send some data:

boost::asio::streambuf b;
std::ostream os(&b);
boost::system::error_code error;


for (;;)
{
    try
    {
        std::string message = "";
        bool empty = false;

        r->command_mutex.lock();
            empty = r->commands->empty();
        if(!empty){
            Command c;

            c = r->commands->front();
            r->commands->pop();

            os << c;

            size_t n = boost::asio::write(socket_, b, error);
            b.consume(n);    
        }
        r->command_mutex.unlock();

        boost::this_thread::interruption_point();

    }
    catch(boost::thread_interrupted&)
    {
        std::cout << "Thread is stopped" << std::endl;

        return;
    }
}

As you pointed the problem is with the sending data, but I do not know why... The reason why it is not sending anything is because r->commands is empty, well it looks that it contains just data which were in the queue before fork and everything what is added after fork is not in that queue..

so it is possible that fork() will copy the structure on which r points? that it is "not a pointer anymore" or that location on which it pointed is changed?

Thanks

EDIT2: So, sending is working but sharing data structures like pointers between processes is not working. Solution can be boost inrerprocess but I think better way is threads...

EDIT3:

I created a picture so you can better understand what I want to do: enter image description here

1.) clients connect to the end point 2.) server for each client create thread 3.) copy_thread copies same date to each client private queue 4.) clients_thread sending data to the clients

Global data are stored in r->commands pointer, data are gathered dynamically on "fly" from multiple methods... I want to send same gathered data to all clients until I will not stop the server thread - endpoint...

Hope that you will understand what I am trying to do. Thanks

sehe
  • 374,641
  • 47
  • 450
  • 633
Dusan Plavak
  • 4,457
  • 4
  • 24
  • 35
  • The fork model is very limited in what it can do and we have better ways nowadays. Especially now that you are using boost asio, you should probably abandon the fork model altogether. – usr May 24 '14 at 13:25
  • So what do you suggest then? Multiple threads? Like in HTTP3 server example on boost? – Dusan Plavak May 24 '14 at 13:28
  • Normally, you'd use async IO for socket servers that have potentially many clients. Async IO requires a small, constant number of threads. It is most efficient and flexible. – usr May 24 '14 at 13:33
  • Your problem is in the sending part. You don't show it. Therefore, we can't help. (The forking works just fine on my linux box) – sehe May 24 '14 at 15:57
  • Yes, your code is working so problem is in sending part I added code following the while loop – Dusan Plavak May 24 '14 at 18:22
  • Oh. Wokay. I'm a bit confused now, since you accepted my answer. However, here's some notes: you weren't unlocking the mutex in the exception handler. You are "sharing" (cough) data (`commands`) and a mutex across forks. That doesn't work. More specifically: `pop()` only pops in the child process :/ If the fork happens at a time when the mutex is locked, then the result is most likely deadlock in the child (or undefined behaviour). You probably want Boost Interprocess, or **just** threads: http://stackoverflow.com/a/23117587/85371 – sehe May 24 '14 at 19:25
  • I made an edit so you can better see what I`m trying to do – Dusan Plavak May 24 '14 at 20:11
  • I contest the notion that this picture made it easier to see what you're trying to do :S – sehe May 24 '14 at 20:39
  • :D (facepalm) ok very simply, I have data queue and I want to send a copy of each element in queue to each client. But queue is dynamically filled... – Dusan Plavak May 24 '14 at 20:42
  • Are you writing a chat server :) – sehe May 24 '14 at 20:45
  • No, I am catching function calls on one app and sending it to all clients and calling that functions on clients also... – Dusan Plavak May 24 '14 at 20:46
  • I've updated the async demo to [use c++03 here](http://stackoverflow.com/questions/23845501/boost-fork-for-new-client-on-server-closing-socket/23847110#comment36711569_23847110) – sehe May 25 '14 at 12:46

2 Answers2

2

For reference, here is a self contained example that just sends a large file:

std::ifstream ifs("/etc/dictionaries-common/words");
std::string cpp(std::istreambuf_iterator<char>(ifs), {});
socket_.write_some(boost::asio::buffer(cpp));

and it works correctly:

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <fstream>

using boost::asio::ip::tcp;

struct R { 
    bool serverRunning = false;
};

int main()
{
    auto serverPort = 6767;

    boost::asio::io_service io_service;

    auto r = std::unique_ptr<R>(new R);

    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), serverPort));

    r->serverRunning = true;
    tcp::socket socket_(io_service);
    io_service.run();

    while(1)
    {
        if(boost::this_thread::interruption_requested())
        {
            acceptor.close();                    

        }
        acceptor.accept(socket_);

        io_service.notify_fork(boost::asio::io_service::fork_prepare);

        if (fork() == 0)
        {
            io_service.notify_fork(boost::asio::io_service::fork_child);
            acceptor.close();

            std::ifstream ifs("/etc/dictionaries-common/words");
            std::string cpp(std::istreambuf_iterator<char>(ifs), {});
            socket_.write_some(boost::asio::buffer(cpp));
            break;
        }
        else
        {
            io_service.notify_fork(boost::asio::io_service::fork_parent);
            socket_.close();
            continue;

        }
    }
}

When checked with netcat localhost 6767 | md5sum, the client receives exact copies of the file each time.

I think your real problem isn't the forking, but the sending.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • @DusanPlavak Well, **[here's a demonstration using Asio's _async_ capabilities](http://coliru.stacked-crooked.com/a/7a3dd375aa4a1c8a)**. Ironically, it handles 25,000 concurrent client connections on a single `io_worker` thread for me, so you could actually remove the mutex on `connections`. (Adding more io-worker threads reduced throughput and increased CPU load). I tried with `for a in {1..100000}; do nc localhost 6767& done | uniq -c` (run the server as root with e.g. `ulimit -n 1024000` or your linux box might restrict the connections) – sehe May 25 '14 at 00:12
  • does not need to have a different socket for each connection? – Dusan Plavak May 25 '14 at 03:13
  • I tried your code with small changes because I am not using c++11, so in add client I changed std::make(ss) for &ss and connections is vector of pointers... hovewer it looks like working just with a small problem... if I connect two clients data are sending just to the one, after I disconnect it, it start to sending data to the second one... idk why – Dusan Plavak May 25 '14 at 03:28
  • @DusanPlavak "does not need to have a different socket for each connection?" (And a different port for each different socket ?) Of course NO, think of each new connection as a creation of a new space, kind of new thread communicating with other end of socket. – Jean Davy May 25 '14 at 07:39
  • Following previuous: I didn't check extensively your code but before calling io_service.run(), you must have set some works to do, otherwise io_service.run() will exit immediately. Take a very attentive look at http://stackoverflow.com/questions/15568100/confused-when-boostasioio-service-run-method-blocks-unblocks?rq=1. Welcome to the asio wonderful world ! – Jean Davy May 25 '14 at 07:50
  • @jeanDavy Ah. I confused you for the OP there, earlier. Sorry for the confusion. Perhaps your comments about the OP's code would have been better placed at the question :) – sehe May 25 '14 at 09:20
  • @DusanPlavak Yes you need a different socket (it's the `connections` vector). And, **[here's a c++03 version](http://coliru.stacked-crooked.com/a/faf4edafc0eed230)**: the fact that `tcp::socket` isn't copyable leads to some more work here. Note that you could do without the additional allocation overhead, but that's an optimization making the code less robust/readable. – sehe May 25 '14 at 09:51
  • Thanks, it is working just I do not know why if I run Server in thread and then I interrupt that thread and also want to join it... it does not join. Instead join is blocking app... http://pastebin.com/3fj58GRE – Dusan Plavak May 25 '14 at 12:59
  • @DusanPlavak The server thread is _inside_ the `Server` class in my sample. The destructor already interrupts the relevant threads and joins them, which is exactly why my sample shuts down cleanly after waiting a fixed number of seconds on the main thread. – sehe May 25 '14 at 13:06
  • Yeah, but I need to have server on own thread because otherwise whole my app will freeze. But anyway it should be the same thing, you are creating server in main thread, when it exit server is destroyed... I created new thread, and server inside of it. After that I interrupted thread and asked it for join.. so it should be the same thing? – Dusan Plavak May 25 '14 at 13:10
  • Admin hat on: you should really learn to post selfcontained examples, because the conditionals in `startServer` are obviously tautological (your real code is _probably_ different?). Your (redundant?) `server_thread` is a tight loop (ouch). There's nothing outside that that is "obviously wrong" (but I _do_ recall your original sample locked the mutex and failed to unlock in the `catch` block - this could be the cause for deadlock, but it's guesswork _because your sample is not selfcontained_. See [Solve Your Problem by ALMOST Asking a Question on StackOverflow](http://tinyurl.com/ovgwac5) – sehe May 25 '14 at 13:11
  • @DusanPlavak Let me say it again: The server thread is _inside the `Server` class_. So, it's *already* on its own thread. (How _else_ could I possibly wait in `main()`?) – sehe May 25 '14 at 13:12
  • Ok I get it. So now I am not creating a new thread for it just use new Server(6677) and store this value in pointer, so server will exist until the pointer will not be deleted... I have one more question in this code http://pastebin.com/MeEikFQr first example is working, but second will freeze whole app if I delete Server. but why? Thanks – Dusan Plavak May 25 '14 at 13:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/54385/discussion-between-sehe-and-dusan-plavak). – sehe May 25 '14 at 13:47
0

The parent returns, so it leaves the function with the loop. After this, the parent doesn't accept the next connection (because it doesn't reach acceptor.accept(); another time) so it doesn't send any data to the this client.

Marcel Krüger
  • 879
  • 8
  • 16