Reconnect ZMQ_ROUTER socket with custom identity - c++

I'm writing a code which uses routers' identities to dynamically manage the peers.
To do that, I build the messages with target identity in the first frame, and then I send them trough a router socket to the appropriate peer. If a peer with that identity doesn't exist, I'll create a new one.
In code is something like that:
Main.cpp
zmq::socket_t sendSocket(*_pZmqContext.get(), ZMQ_ROUTER);
// It forces to throw an exception when peer doesn't exists, so I can create a new one.
sendSocket.setsockopt(ZMQ_ROUTER_MANDATORY, 1);
sendSocket.bind("inproc://processors");
...
try
{
...
isSuccess = message2send.send(sendSocket);
...
}
catch (zmq::error_t& ex)
{
if (ex.num() == EHOSTUNREACH)
{
// new peer is created (see OtherRouter.cpp)
...
}
}
...
OtherRouter.cpp
// This is how reader sockets are created...
zmq::socket_t reader(*_pZmqContext, ZMQ_ROUTER);
reader.setsockopt(ZMQ_ROUTING_ID,(std::byte *)&newIdentityValueForSocket[0],sizeof(newIdentityValueForSocket)); ;
reader.connect("inproc://processors");
assert(reader.connected());
...
This works fine, but I need some extra.
Some peers might be destroyed due to inactivity, and being recreated later, when activity is back.
When this happen, code is not working as expected. Even the peer is created successfully, I'm getting EHOSTUNREACH exception. It's like sockets can't communicate again..
So, It seems the sender socket knows that the older peer has been disconnected, but It can't connect to new one.
Any suggestion about how to solve it ?
Thanks!

Related

How QTcpSocket client can understand that it is in the queue? (and does not go to connected state)

I am developing a server and client project with Qt. I want to print the connection status for the users. For this propose I use state() like:
socketState = mySocket.state();
if (socketState == 3) {
Print("we have connected");
}
However, it does not work when the server queues new connections. To make it clear, my client state is 3 even if the server has paused accepting new connections:
//server side:
myServer->pauseAccepting();
//client side:
connectToHost()
socketState = mySocket.state();
Now the socketState is 3 instead of 0 or a special number for queue state.
To sum it up, I want to know how to inform the client that it is in the queue? Is there anything like state() that has a return value for queue state?
Finally I could find the answer.
The refused client that goes to the queue (by OS) is not different with other clients. Therefore, we can make a special socket for it (which means it is NOT in queue anymore) and start communication (inform and close).
for example:
QTcpSocket clientSocket
QTcpSocket queueSocket
In my project, the server first send a message to the queue client.
The message is:
"We can NOT accept a new client because we can NOT handle more than one". So, it knows what is the problem.
Then, the server closes the queue client socket.
We SHOULD close it because we do NOT want to handle lots of clients.
However, the main point is that we can work with clients in the queue and decide how to deal with them . I prefer to accept them just to inform and close them.
if (we have NOT a client) {
work with clientSocket
}
else {
queueSocket.wirte("We can NOT accept a new client because we can NOT handle more than one")
close queueSocket
}
I hope it helps those who want to inform rejected clients.

Identify state of socket in half-open connection using POCO Frame

I have a problem then I'm trying reconnect with server, using Poco underlying in my Proxy. In the end of connect sessions I recieve FIN,ACK package from server. Then TCP send ACK to answer server about recieving their FIN. So i have half-open connection. My socket is closed to read but not to send. After a few second of waiting, I send http request,
auto& requestStream = mSession->sendRequest(request);
Poco::StreamCopier::copyStream(request.stream(), requestStream);
auto& responseStream = mSession->receiveResponse(response);
Poco::StreamCopier::copyStream(responseStream, response.send());
TCP send FIN,ACK and send SYN to start another connect session so i recieve exception on SSL_Read().
One way to resolve that is identify state of socket using shutdown
if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)
reconnect();
but its not legal, bcs SSL* ssl is private field of SecureSocketImpl _impl that is private field of ServerSocketImpl too.
Have you ever encountered this problem?
This is screen of wireshark.
Ok, this problem wont be resolved until POCO pay attention to it. I suggest my way.
In my case, server doesnt sent SSL_shutdown, so my socket is open.
You should previously check that socket is empty and suppress this exception. After that u can check, that server has sent SSL_shutdown() before reconnection. Then u should use reconnect to recover your session.
Here is description how to make hack to check the state of your socket.
class HttpsClientSession : public HTTPSClientSession {
public:
using Super::HTTPSClientSession;
ostream& sendRequest(HTTPRequest& request) override {
if (connected() && socket().poll(Poco::Timespan(0), Socket::SELECT_READ)) {
try {
peek();
} catch (const SSLConnectionUnexpectedlyClosedException&) {
reconnect();
}
auto sock = static_cast<SecureStreamSocketImpl*>(socket().impl());
auto ssl = PRIVATE_ACCESS(PRIVATE_ACCESS(*sock, _impl), _pSSL);
if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)
reconnect();
}
return HTTPSClientSession::sendRequest(request);
}
};
¯\_(ツ)_/¯

How to properly use QWebSocket::sendBinaryMessage() method from a C++ client?

There are C++ Qt client & server. Following code works fine and the connection happens between the client and the server:
QWebSocket webSocket; // defined somewhere
...
QUrl url;
url.setScheme("ws"); // SSL encryption disabled
url.setHost(serverName); // "127.0.0.1" (can be "www.abc.com" too)
url.setPort(portNumber); // 2000
webSocket.open(url); // connects with the server properly
PRINT(url.toString()); // output: "ws://127.0.0.1:2000"
While sending the binary data, the function returns 0 instead of the number of bytes:
// though the message.size() is 80 bytes; the method returns 0
webSocket.sendBinaryMessage(QByteArray(message.data(), message.size()));
Note that, the QWebSocketServer works as expected.
We also have a Javascript client. That connects & sends the binary message properly. The only addition in that client is below:
webSocketJS.binaryType = "arraybuffer"; // <--- Javascript code
But such provision is not found in QWebSocket or I may have missed it.
Question: How to correctly send the binary data over the web connection?
For those interested, the server [pseudo] code is like below:
auto pWebSocket = WebServer.nextPendingConnection();
QObject::connect(pWebSocket, &QWebSocket::binaryMessageReceived,
[&] (const QByteArray& message) { DataRead(message, rManager); }); // This slot is not called as of now
It seems that there is no mention of how the QWebSocket::connected() signal is treated.
Due to internet delay and initial handshakes, the WebSocketServer may take some time to establish a connection. Ideally the binary/text message should be sent only after the connected() is received.
Before making a connection using webSocket.open(url), you should be handling this signal:
... // same code
QObject::connect(&webSocket, &QWebSocket::connected,
[&] ()
{
webSocket.sendBinaryMessage(QByteArray(message.data(), message.size()));
// ... set some internal state suggesting the established connection
}
webSocket.open(url);
Above is just a pseudo code to show that the first sendBinaryMessage() should be sent after the connect() signal. Ideally in real world code, you may want to set some state, which informs the client that the connection is established.
Similarly as mentioned in the comments, we should be checking for errors and disconnections as well.

boost asio bind: bad file descriptor

I'm creating multithreading application, that will work as "router" but on application layer.
The software will run on multiple hosts with multiple network interfaces.
In my application in one thread runs server, that accepts every connection on every interface and pass it to another worker-thread, which decide if the message will be forwarded and which interface schould be used.
I decided to use boost-asio.
So summarizing: incoming messages only in server (only here the read function), outgoing depending on typ through different gateway (only send function). The gateway should always connect to one interface, therefore I try to use the bind method.
But I get an exception: "bind: Bad file descriptor"
Here is the code snippet:
try
{
MY_LOG(trace) << "Connecting on IFace" << routingConf->connections[interface].ipSource;
boost::asio::io_service *svc = new boost::asio::io_service();
this->socket = new boost::asio::ip::tcp::socket(*svc);
boost::asio::ip::tcp::endpoint localEndpoint =
ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 0);
boost::asio::ip::tcp::endpoint remoteEndpoint = ip::tcp::endpoint(
boost::asio::ip::address::from_string("127.0.0.1"), port);
this->socket->bind(localEndpoint);
MY_LOG(trace) << "socket success";
this->socket->connect(remoteEndpoint);
} catch (std::exception &e)
{
MY_LOG(fatal) << e.what();
}
I tried different settings for local and remote endpoint. The server is already running in another thread, and if I not perform the binding operation, it is getting the sent message from send function (not included here).
socket::bind throws bad file descrption when socket was created but it is not open. You used constructor
basic_stream_socket(
boost::asio::io_service & io_service);
which constructs socket without opening it.
You should call open method before calling bind or use one of overloaded version of socket constructor which creates and also opens socket.

boost::asio async_accept Refuse a connection

My application have an asio server socket that must accept connections from a defined List of IPs.
This filter must be done by the application, (not by the system), because it can change at any time (i must be able to update this list at any time)
The client must receive an acces_denied error.
I suppose when the handle_accept callback is called, SYN/ACK has already be sent, so don't want to accept then close brutally when i detect the connected ip est not allowed. I don't manage the client behavior, maybe it doesn't act the same when the connection is refused and just closed by peer, so i want to do everything clean.
(but it's what im soing for the moment)
Do you know how i can do that???
My access list is a container of std::strings (but i can convert it to a countainer of something else....)
Thank you very much
The async_accept method has an overload to obtain the peer endpoint. You can compare that value inside your async_accept handler. If it does not match an entry in your container, let the socket go out of scope. Otherwise, handle it as required by your appliation.
I don't know the details of your app, but this is how I'd do it.
In the accept handler/lambda
void onAccept(shared_ptr<connection> c, error_code ec)
{
if (ec) { /*... */ }
if (isOnBlackList(c->endpoint_))
{
c->socket_.async_write( /* a refusal message */,
[c](error_code, n)
{
c->socket_.shutdown();
// c fizzles out of all contexts...
});
}
else
{
// successful connection execution path
}
}