Find the best way to reconnect Boost::Beast websocket - c++

I'd like get an advice about how to replace boost::beast based ssl websocket connection between 2 different servers that will minimize the reconnection time.
my web socket client object is from the following type as a member variable of std::optional<websocketMgr> mgr; and it defined as follows :
class websocketMgr {
...
private:
boost::beast::websocket::stream<
boost::beast::ssl_stream<boost::beast::tcp_stream>> ws_;
}
during steady state, the web socket may send asnyc_write calls and is halting in read
ws_.async_read(buffer, yield);
From time to time, I'd like to change the connection to a different server peer. So I first need to trigger an exception on the async_read that is waiting by the current socket thread - this will eventually reach the socket d'tor and close the current connection before setting a new one.
void websocketMgr::closeWebsocket() {
ws_.close(boost::beast::websocket::close_code::normal);
...
mgr.emplace(...); // create new websocket, after the older one fully removed
However, as I experienced, the ssl_stream (underlying object) can take very long time to complete since it may reach timeout if the server side is currently using the connection and Is not cooperating in the shutdown process.
Perhaps anyone can suggest a way that will ensure that the older connection is closed gracefully on the background, while the new connection is created as fast as possible to reduce lack of connectivity.
Thanks !

Related

Multithread server in C++, how to terminate threads and clean up nicely

The server I've written in c++ server works like proxy. Main function:
try
{
Connector c(ip); //establishes persistent connection to the server B
Listener1 l1(port); //listens incoming connection to the server A (our proxy) and push them into the queue
Listener2 l2(&c); //listens responses (also push messages) from the server B and push them into the queue
Proxy p(&c, &l1, &l2); //pulls clients out from the queue and forwards requests to the server B, pull out everything from the listener2 queue and returns as a responce
KeepAlive k(&l1, &p); //pushes the empty client to the listeners1's queue thus the proxy sends keepalive to the server B and the response is discarded
l1.start();
p.start();
l2.start();
k.start();
l1.join();
p.join();
l2.join();
k.join();
catch(std::string e)
{
std::cerr << "Error: " << e << std::endl;
return -1;
}
For now I have problems/doubts as follows:
**1.**I throw an exception from constructors, is it good practise? I throw an exception when it's not possible to establish the connection, that's why the object shouldn't be created I guess.
**2.**There is a problem with closing the application safety and clean up when the connection time-out occurs or the server B closes the connection and so on. listener1 and listener2 use blocking functions (system call accept() and BIO_read from openssl lib) so it's not possible to just set the loop condition from another thread. The problem is also the fact that all the modules are connected and share resources using mutexes. My current piece of code just calls exit function to terminate whole application.
I know this is not a perfect solution, I appreciate any advices and tips.
Thanks in advance
Constructors should throw exceptions if they fail. C++ is designed to handle that well. Base classes and members are cleaned up if and only if they're already constructed.
Blocking functions from other libraries are always a problem. Windows and POSIX handle it well: WSAWaitForMultipleObjectEx and select allow you to add an extra handle, which you can use to unblock the wait.
In your accept call, you might fake this by creating a connection from the main thread, via localhost. Detecting this "unusual" connection would be a signal to stop accepting further connections.
As for the openSSL read, I'd just close the socket from the main thread, threadsafety be damned. I would make sure that I'd do this quite late in the shutdown, and I wouldn't expect the library to be usable at all after that point.

boost::asio::ip::tcp::socket -> how to query the socket state?

I am using the boost library to create a server application. At a time one client is allowed therefore if the async_accept(...) function gets called the acceptor will be closed.
The only job of my server is to send data periodically (if sending is enabled on the server, otherwise "just sit there" until it gets enabled) to the client. Therefore I have a boost message queue - if a message arrives the send() is called on the socket.
My problem is that I cannot tell if the client is still listening. Normally you would not care, by the next transmission the send would yield an error.
But in my case the acceptor is not opened when a socket is opened. If the socket gets in the CLOSE_WAIT state I have to close it and open the acceptor again so that the client can connect again.
Waiting until the next send is also no option since it is possible that the sending is disabled therefore my server would be stuck.
Question:
How can I determine if a boost::asio::ip::tcp::socket is in a CLOSE_WAIT state?
Here is the code to do what Dmitry Poroh suggests:
typedef asio::detail::socket_option::integer<ASIO_OS_DEF(SOL_SOCKET),SO_ERROR>so_error;
so_error tmp;
your_socket.get_option(tmp);
int value=tmp.value();
//do something with value.
You can try to use ip::tcp::socket::get_option and get error state with level SOL_SOCKET and option name SO_ERROR. I'm surprised that I have not found the ready boost implementation for it. So you can try to meet GettableSocketOption requirements an use ip::tcp::socket::get_option to fetch the socket error state.

threads for tcp communication

My question is about usage of threads. Im making an application that connects to a device over TCP/IP. Im using boost::asio lib. I have decided to use a read or listening thread and a write thread for listening and writing to the device respectively. My confusion is should the function creating the socket that handles the communication also be a thread.
Thanks :)
In my client class, I create 2 worker threads to handle sending and receiving messages which are used for multiple connections to multiple servers. The thread that creates those 2 worker threads happens to be the user interface thread. This is what my code looks like:
// Create the resolver and query objects to resolve the host name in serverPath to an ip address.
boost::asio::ip::tcp::resolver resolver(*IOService);
boost::asio::ip::tcp::resolver::query query(serverPath, port);
boost::asio::ip::tcp::resolver::iterator EndpointIterator = resolver.resolve(query);
// Set up an SSL context.
boost::asio::ssl::context ctx(*IOService, boost::asio::ssl::context::tlsv1_client);
// Specify to not verify the server certificiate right now.
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
// Init the socket object used to initially communicate with the server.
pSocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(*IOService, ctx);
//
// The thread we are on now, is most likely the user interface thread. Create a thread to handle all incoming socket work messages.
if (!RcvThreadCreated)
{
WorkerThreads.create_thread(boost::bind(&SSLSocket::RcvWorkerThread, this));
RcvThreadCreated = true;
WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread, this));
}
// Try to connect to the server. Note - add timeout logic at some point.
boost::asio::async_connect(pSocket->lowest_layer(), EndpointIterator,
boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholders::error));
The worker threads handle all socket I/O. It depends on what you are doing, but the 2 worker threads for servicing the socket will need to be created from another thread. That other thread can be the user interface thread or main thread if you want since it will be returning pretty quickly. If you have multiple connections to servers or clients, then It is up to you to decide whether or not you want more than one set of threads to service them.
That depends on whether you want to read and write at the same time. In that case you would need one thread for reading and one for writing, but you would have to properly synchronize those in case both streams to and from the device have something to do with each other (what they probably do). However, talking to a device sounds to me like a task where you establish a connection, send some request, wait for and read the answer, send another request, wait for and read the next answer, etc. In that case using just one thread is sufficient and makes your life a lot easier.

Ensuring data is being read with async_read

I am currently testing my network application in very low bandwidth environments. I currently have code that attempts to ensure that the connection is good by making sure I am still receiving information.
Traditionally I have done this by recording the timestamp in my ReadHandler function so that each time it gets called I know I have received data on the socket. With very low bandwidths this isn't sufficient because my ReadHandler is not getting called frequently enough.
I was toying around with the idea of writing my own completion condition function (right now I am using tranfer_at_least(1)) thinking it would get called more frequently and I could record my timestamp there, but I was wondering if there wasn't some other more standard way to go about this.
We had a similar issue in production: some of our connections may be idle for days, but we must detect if the remote is dead ASAP.
We solved it by enabling the TCP_KEEPALIVE option:
boost::asio::socket_base::keep_alive option(true);
mSocketTCP.set_option(option);
which had to be accompanied by new startup script that writes sensible values to /proc/sys/net/ipv4/tcp_keepalive_* which have very long timeouts by default (on LInux)
You can use the read_some method to get partial reads, and deal with the book keeping. This is more efficient than transfer_at_least(1), but you still have to keep track of what is going on.
However, a cleaner approach is just to use a concurrent deadline_timer. If the timer goes off before you are finished, then is taking too long and cancel whatever is going on. If not, just stop the timer and continue. Something like:
boost::asio::deadline_timer t;
t.expires_from_now(boost::posix_time::seconds(20));
t.async_wait(bind(&Class::timed_out, this, _1));
// Do stuff.
if (!t.cancel()) {
// Timer went off, abort
}
// And the timeout method
void Class::timed_out(error_code const& error)
{
if (error == boost::asio::error::operation_aborted) return;
// Deal with the timeout, close the socket, etc.
}
I don't know how to handle low latency of network from within application. Can you be sure if it's network latency, or if peer server or peer application busy and react slowly. Does it matter if it network/server/application quilt?
Even if you can discover network latency and find it's big, what are you going to do?
You can not improve the situation.
Consider other critical case which is a subset of what you're trying to handle - network is down (e.g. you disconnect cable from your machine). Since it a subset of your problem you want to handle it too.
Let's examine the network down effect on active TCP connection.How can you discover your active TCP connection is still alive? Calling send() will success, but it merely says that the message queued in TCP outgoing queue in kernel. TCP stack will try to send it, but since TCP ACK won't be sent back, TCP stack on your side will try to resend it again and again. You can see your message in netstat output (Send-Q column).
I'm aware of the following ways to deal with it:
One standard way is TCP keep alive proposed #Cubby.
Another way is to implement Keep Alive mechanism. Send Keep Alive req message and peer is obligated to send back Keep Alive ack message.
If you don't receive ack message after predefined timeout, try to send Keep Alive req N more times (e.g. N=2). If still no success, close the socket and open it again. If peer server is not available you'll not be abable to open connection, since TCP 3 way handshake requires peer to respond.

Multi threaded client server

Hi I am working on an assignment writing multi threaded client server.
So far I have done is open a socket in a port and forked two thread for listening and writing to client. But I need to connect two type of clients to the server and service them differently. My question is what would be my best approach?
I am handling connection in a class which has a infinite loop to accept connection. When ever a connection is accepted, the class create two thread to read and write to client? Now if I wnat to handle another client of different type, what should we do?
Do I need to open another port? or is it possible to service through same port? May be if it is possible to identify the type of client in the socket than I can handle messages differently.
Or do you suggest like this?
Fork two thread for two type of client and monitor inbound connection in each thread in different port.
when a connection accepted each thread spawn another two thread for listening and writing.
please make a suggestion.
Perhaps you'll get a better answer from a Unix user, but I'll provide what I know.
Your server needs a thread that opens a 'listening' socket that waits for incoming connections. This thread can be the main thread for simplicity, but can be an alternate thread if you are concerned about UI interaction, for example (in Windows, this would be a concern, not sure about Unix). It sounds like you are at least this far.
When the 'listening' socket accepts a connection, you get a 'connected' socket that is connected to the 'client' socket. You would pass this 'connected' socket to a new thread that manages the reading from and writing to the 'connected' socket. Thus, one change I would suggest is managing the 'connected' socket in a single thread, not two separate threads (one for reading, one for writing) as you have done. Reading and writing against the same socket can be accomplished using the select() system call, as shown here.
When a new client connects, your 'listening' socket will provide a new 'connected' socket, which you will hand off to another thread. At this point, you have two threads - one that is managing the first connection and one that is managing the second connection. As far as the sockets are concerned, there is no distinction between the clients. You simply have two open connections, one to each of your two clients.
At this point, the question becomes what does it mean to "service them differently". If the clients are expected to interact with the server in unique ways, then this has to be determined somehow. The interactions could be determined based on the 'client' socket's IP address, which you can query, but this seems arbitrary and is subject to network changes. It could also be based on the initial block of data received from the 'client' socket which indicates the type of interaction required. In this case, the thread that is managing the 'connected' socket could read the socket for the expected type of interaction and then hand the socket off to a class object that manages that interaction type.
I hope this helps.
You can handle the read-write on a single client connection in one thread. The simplest solution based on multiple-threads will be this:
// C++ like pseudo-code
while (server_running)
{
client = server.accept();
ClientHandlingThread* cth = CreateNewClientHandlingThread(client);
cth->start();
}
class ClientHandlingThread
{
void start()
{
std::string header = client->read_protocol_header();
// We get a specific implementation of the ProtocolHandler abstract class
// from a factory, which create objects by inspecting some protocol header info.
ProtocolHandler* handler = ProtocolHandlerFactory.create(header);
if (handler)
handler->read_write(client);
else
log("unknown protocol")
}
};
To scale better, you can use a thread pool, instead of spawning a new thread for each client. There are many free thread pool implementations for C++.
while (server_running)
{
client = server.accept();
thread_pool->submit(client);
cth->start();
}
The server could be improved further by using some framework that implements the reactor pattern. They use select or poll functions under the hood. You can use these functions directly. But for a production system it is better to use an existing reactor framework. ACE is one of the most widely known C++ toolkits for developing highly scalable concurrent applications.
Different protocols are generally serviced on different ports. However, you could service both types of clients over the same port by negotiating the protocol to be used. This can be as simple as the client sending either HELO or EHLO to request one or another kind of service.