Discussion:
[asio-users] Multiple tcp acceptor in one program.
Manh NguyenTien
2015-12-30 08:42:23 UTC
Permalink
Hi there, I'm using Asio to develop a server program. But in a single
application (single-threaded), I need 2 server object listening on different
ports and operate differently, asynchronous of course.

I'm wondering that if there are any problems if I write 2 class having 2 tcp
acceptors, instantiate two of them in the main function and then call
io_service.run().
Is it possible and safe?
Igor R
2016-01-02 16:32:06 UTC
Permalink
Post by Manh NguyenTien
Hi there, I'm using Asio to develop a server program. But in a single
application (single-threaded), I need 2 server object listening on different
ports and operate differently, asynchronous of course.
I'm wondering that if there are any problems if I write 2 class having 2 tcp
acceptors, instantiate two of them in the main function and then call
io_service.run().
Is it possible and safe?
Yes, it is ok. But note that all the completion handlers will be
invoked sequentially, in one thread. Which means that the application
won't be scalable, an if some handler takes too long to complete --
all other will have to wait.
Manh Nguyen Tien
2016-01-02 16:58:39 UTC
Permalink
Hi Igor.
Thanks for your reply. I'm defining ASIO_DISABLE_THREADS in my application now.
Does it mean that if I discard that macro and change no code, Asio will call
my handlers in other threads automatically?

Should I discard that macro? If it's still single-threaded but having timer on
every operation, are there any other benefits/pitfalls compared to multi-
threaded application besides the waiting time for the timeout handler?

At first I was using single-threaded app just to get rid of threading library
linking to reduce binaries's size, seems like it's now not a good reason
anymore :D
Post by Igor R
Post by Manh NguyenTien
Hi there, I'm using Asio to develop a server program. But in a single
application (single-threaded), I need 2 server object listening on
different ports and operate differently, asynchronous of course.
I'm wondering that if there are any problems if I write 2 class having 2
tcp acceptors, instantiate two of them in the main function and then call
io_service.run().
Is it possible and safe?
Yes, it is ok. But note that all the completion handlers will be
invoked sequentially, in one thread. Which means that the application
won't be scalable, an if some handler takes too long to complete --
all other will have to wait.
----------------------------------------------------------------------------
-- _______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Igor R
2016-01-03 19:56:08 UTC
Permalink
Post by Manh Nguyen Tien
Thanks for your reply. I'm defining ASIO_DISABLE_THREADS in my application now.
Does it mean that if I discard that macro and change no code, Asio will call
my handlers in other threads automatically?
No.
ASIO_DISABLE_THREADS means that Asio disallows operations that spawn
internal threads, like async_resolve (if I'm not mistaken, such an
operation will throw boost::asio::error::operation_not_supported
exception).
Even without this macro Asio won't parallelize your completion
handlers, unless you invoke io_service::run() in multiple threads.
Please, read the documentation for details:
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/tutorial/tuttimer5.html
Post by Manh Nguyen Tien
Should I discard that macro?
That depends on your requirements and limitations. Usually there's no
reason to define this macro, unless your platform has restrictions
with regards to threads.
Post by Manh Nguyen Tien
If it's still single-threaded but having timer on every operation, are there any other benefits/pitfalls compared to multi-threaded application besides the waiting time for the timeout handler?
There are 2 "levels" of "single-threadness":
1) One can invoke io_service::run() in one thread, but build w/o
ASIO_DISABLE_THREADS. This allows Asio to spawn internal threads, when
it needs to emulate asynchronous behavior, so all Asio features can be
used. Still, it guarantees that all the completion handlers get
invoked sequentially. So, from Asio user's point of view, the
application is still single-threaded, in the sense that there's no
need to provide any synchronization.
2) One can completely disable threads in Asio with the above macro. In
this case, Asio guarantees that it doesn't spawn any threads, but you
should assume that it may throw operation_not_supported exception
under some circumstances.
Manh Nguyen Tien
2016-01-04 05:55:41 UTC
Permalink
OK, I've been reading the example, so the only problem when using multiple
threads calling io_service::run() is the threads may use a resource at the
same time, in the example's case is the count variable and the cout stream.

If my handlers always use their own resources there will be no problems and I
can gain performance and scalability, is that right?

Here is a portion the code: http://codepad.org/oeFITS4v
It is a server that receives a command string from client, executes the
command then writes the output back (I removed ASIO_DISABLE_THREADS)

The on_read handler uses read_buffer_ and the on_write handler uses
write_buffer_, I think this is safe enough.

On the client side, after sending a command it will wait for results, so one
client won't send 2 commands at the same time, no multiple server's on_read
calling. The async_accept is not called again until the current client
disconnects, the ctlserver deals with only one client (the control client
which sends command) at a time.

Till now if I call io_service::run() in multiple threads, it's still safe.

The point is there's another server (the main server) is accepting multiple
clients (normal clients which requests service) at the same time and it runs
in the same program with the ctlserver.
Each connected client will be allocated with 1 socket, 2 buffers to read and
write, separately.
(I wrote a session class to handle client).
My worry is when serving multiple clients, sessions will access some shared
resources, reading and writing, for example database connection.
It's a pity that there's no readable code for this now because I'm still
designing the server.

Basically when a client sends a request to the server, session parses it and
creates a object (request class), request class has a method called execute().
When multiple threads call the request::execute() it may be unsafe.
So I have two options:
- Create a global strand and wrap the execute() function of any connected
session, is it acceptable? Saying: 500 clients and wrap 500 functions, sharing
one database connection.
- The data source is a MySQL server, I can let each session have its own db
connection, and I will try to assure that my application doesn't have any
shared data to be used concurently, each session is on its own and mysql
connector library is thread-safe already.

Can I have some suggestions on designing the server?
Thank you in advance.
Post by Igor R
Post by Manh Nguyen Tien
Thanks for your reply. I'm defining ASIO_DISABLE_THREADS in my application
now. Does it mean that if I discard that macro and change no code, Asio
will call my handlers in other threads automatically?
No.
ASIO_DISABLE_THREADS means that Asio disallows operations that spawn
internal threads, like async_resolve (if I'm not mistaken, such an
operation will throw boost::asio::error::operation_not_supported
exception).
Even without this macro Asio won't parallelize your completion
handlers, unless you invoke io_service::run() in multiple threads.
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/tutorial/tuttimer5.
html
Post by Manh Nguyen Tien
Should I discard that macro?
That depends on your requirements and limitations. Usually there's no
reason to define this macro, unless your platform has restrictions
with regards to threads.
Post by Manh Nguyen Tien
If it's still single-threaded but having timer on every operation, are
there any other benefits/pitfalls compared to multi-threaded application
besides the waiting time for the timeout handler?
1) One can invoke io_service::run() in one thread, but build w/o
ASIO_DISABLE_THREADS. This allows Asio to spawn internal threads, when
it needs to emulate asynchronous behavior, so all Asio features can be
used. Still, it guarantees that all the completion handlers get
invoked sequentially. So, from Asio user's point of view, the
application is still single-threaded, in the sense that there's no
need to provide any synchronization.
2) One can completely disable threads in Asio with the above macro. In
this case, Asio guarantees that it doesn't spawn any threads, but you
should assume that it may throw operation_not_supported exception
under some circumstances.
----------------------------------------------------------------------------
-- _______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Igor R
2016-01-05 19:36:57 UTC
Permalink
Post by Manh Nguyen Tien
If my handlers always use their own resources there will be no problems and I
can gain performance and scalability, is that right?
Yes.
Post by Manh Nguyen Tien
Here is a portion the code: http://codepad.org/oeFITS4v
It is a server that receives a command string from client, executes the
command then writes the output back (I removed ASIO_DISABLE_THREADS)
The on_read handler uses read_buffer_ and the on_write handler uses
write_buffer_, I think this is safe enough.
It looks ok.
Post by Manh Nguyen Tien
Basically when a client sends a request to the server, session parses it and
creates a object (request class), request class has a method called execute().
When multiple threads call the request::execute() it may be unsafe.
- Create a global strand and wrap the execute() function of any connected
session, is it acceptable? Saying: 500 clients and wrap 500 functions, sharing
one database connection.
<...>
Post by Manh Nguyen Tien
- The data source is a MySQL server, I can let each session have its own db
connection, and I will try to assure that my application doesn't have any
shared data to be used concurently, each session is on its own and mysql
connector library is thread-safe already.
The both ways will ensure thread-safety, but both of them create
dependency between the networking module scalability/performance and
some other i/o, which might be much slower, due to its nature. Imagine
what will happen is a DB-related operation takes a lot of time or even
gets stuck...
Perhaps it would be much more convenient to create a dedicated
io_service (or an "active object" encapsulating io_service), whose
sole purpose is to process db-related tasks. Then, your networking
module will just post() to that io_service functors that perform suck
tasks when invoked (use std::bind or lambdas to create them). I assume
that such a task may be executed asynchronously, in a "fire and
forget" manner. If the initiator needs the result of the task
execution, the things get more complicated...
Svante Karlsson
2016-01-05 22:40:45 UTC
Permalink
Post by Igor R
Perhaps it would be much more convenient to create a dedicated
io_service
I make a tiny async postgres library using asio a couple of months ago as a
sample for someone.
https://github.com/bitbouncer/postgres-asio/tree/master/postgres_asio

Take a look at the async connect

void connect(std::string connect_string, on_connect_callback cb);

postgres has no async connect but it is implemented using a background
io_service which never blocks the foreground ioservice.

(What's missing from that library is a decent pooling of connections
otherwise it seems to work very well)


/svante
Post by Igor R
Post by Manh Nguyen Tien
If my handlers always use their own resources there will be no problems
and I
Post by Manh Nguyen Tien
can gain performance and scalability, is that right?
Yes.
Post by Manh Nguyen Tien
Here is a portion the code: http://codepad.org/oeFITS4v
It is a server that receives a command string from client, executes the
command then writes the output back (I removed ASIO_DISABLE_THREADS)
The on_read handler uses read_buffer_ and the on_write handler uses
write_buffer_, I think this is safe enough.
It looks ok.
Post by Manh Nguyen Tien
Basically when a client sends a request to the server, session parses it
and
Post by Manh Nguyen Tien
creates a object (request class), request class has a method called
execute().
Post by Manh Nguyen Tien
When multiple threads call the request::execute() it may be unsafe.
- Create a global strand and wrap the execute() function of any
connected
Post by Manh Nguyen Tien
session, is it acceptable? Saying: 500 clients and wrap 500 functions,
sharing
Post by Manh Nguyen Tien
one database connection.
<...>
Post by Manh Nguyen Tien
- The data source is a MySQL server, I can let each session have its
own db
Post by Manh Nguyen Tien
connection, and I will try to assure that my application doesn't have any
shared data to be used concurently, each session is on its own and mysql
connector library is thread-safe already.
The both ways will ensure thread-safety, but both of them create
dependency between the networking module scalability/performance and
some other i/o, which might be much slower, due to its nature. Imagine
what will happen is a DB-related operation takes a lot of time or even
gets stuck...
Perhaps it would be much more convenient to create a dedicated
io_service (or an "active object" encapsulating io_service), whose
sole purpose is to process db-related tasks. Then, your networking
module will just post() to that io_service functors that perform suck
tasks when invoked (use std::bind or lambdas to create them). I assume
that such a task may be executed asynchronously, in a "fire and
forget" manner. If the initiator needs the result of the task
execution, the things get more complicated...
------------------------------------------------------------------------------
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Manh Nguyen Tien
2016-01-06 08:26:24 UTC
Permalink
Thank you Svante, your code gives me some ideas for my implementation, I will
take my time to read it. This makes me realize that async is not only for
networking, and I didn't make my design fully async, I should work on it more
and will try applying your design.
Post by Igor R
The both ways will ensure thread-safety, but both of them create
dependency between the networking module scalability/performance and
some other i/o, which might be much slower, due to its nature. Imagine
what will happen is a DB-related operation takes a lot of time or even
gets stuck...
Perhaps it would be much more convenient to create a dedicated
io_service (or an "active object" encapsulating io_service), whose
sole purpose is to process db-related tasks. Then, your networking
module will just post() to that io_service functors that perform suck
tasks when invoked (use std::bind or lambdas to create them). I assume
that such a task may be executed asynchronously, in a "fire and
forget" manner. If the initiator needs the result of the task
execution, the things get more complicated...
I think instead of return the result, the task executor being invoked can
write to the connection's buffer and tell the connection to reply after the
data are ready, I just need to pass a pointer of the connection that needs
data. The another problem is when the user cancels current request and send a
new one, the buffer can be written at the same time by the old request executor
and the new request executor. So I decided to go like a HTTP server, each
connection only serves 1 request, then disconnects, my system doesn't really
need to keep connection alive though.
The thing is quite clear to me now, I'm confident enough to try some designs
for it.

Thank you and thanks everyone who sent me direct emails also :)
Post by Igor R
Post by Igor R
Perhaps it would be much more convenient to create a dedicated
io_service
I make a tiny async postgres library using asio a couple of months ago as a
sample for someone.
https://github.com/bitbouncer/postgres-asio/tree/master/postgres_asio
Take a look at the async connect
void connect(std::string connect_string, on_connect_callback cb);
postgres has no async connect but it is implemented using a background
io_service which never blocks the foreground ioservice.
(What's missing from that library is a decent pooling of connections
otherwise it seems to work very well)
/svante
Post by Igor R
Post by Manh Nguyen Tien
If my handlers always use their own resources there will be no problems
and I
Post by Manh Nguyen Tien
can gain performance and scalability, is that right?
Yes.
Post by Manh Nguyen Tien
Here is a portion the code: http://codepad.org/oeFITS4v
It is a server that receives a command string from client, executes the
command then writes the output back (I removed ASIO_DISABLE_THREADS)
The on_read handler uses read_buffer_ and the on_write handler uses
write_buffer_, I think this is safe enough.
It looks ok.
Post by Manh Nguyen Tien
Basically when a client sends a request to the server, session parses it
and
Post by Manh Nguyen Tien
creates a object (request class), request class has a method called
execute().
Post by Manh Nguyen Tien
When multiple threads call the request::execute() it may be unsafe.
- Create a global strand and wrap the execute() function of any
connected
Post by Manh Nguyen Tien
session, is it acceptable? Saying: 500 clients and wrap 500 functions,
sharing
Post by Manh Nguyen Tien
one database connection.
<...>
Post by Manh Nguyen Tien
- The data source is a MySQL server, I can let each session have its
own db
Post by Manh Nguyen Tien
connection, and I will try to assure that my application doesn't have any
shared data to be used concurently, each session is on its own and mysql
connector library is thread-safe already.
The both ways will ensure thread-safety, but both of them create
dependency between the networking module scalability/performance and
some other i/o, which might be much slower, due to its nature. Imagine
what will happen is a DB-related operation takes a lot of time or even
gets stuck...
Perhaps it would be much more convenient to create a dedicated
io_service (or an "active object" encapsulating io_service), whose
sole purpose is to process db-related tasks. Then, your networking
module will just post() to that io_service functors that perform suck
tasks when invoked (use std::bind or lambdas to create them). I assume
that such a task may be executed asynchronously, in a "fire and
forget" manner. If the initiator needs the result of the task
execution, the things get more complicated...
--------------------------------------------------------------------------
---- _______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Loading...