Switching from QLocalSocket to QTcpSocket

485 views
Skip to first unread message

Marco Vimercati

unread,
Sep 3, 2015, 4:39:16 AM9/3/15
to qjsonrpc-development
Hi all,

I'm having troubles switching my code from rpc based on Local sockets to TCP sockets.
Looking at the examples it seems that the code should be similar and simmetrical for the two layers. Here following the essential (and incomplete) code:

Server QJsonRpcLocalServer :

localServer = new QJsonRpcLocalServer();
localServer->addService(service);
localServer->listen("service name");

Server QJsonRpcTcpServer:

tcpServer = new QJsonRpcTcpServer();
tcpServer->addService(service);
tcpServer->listen(QHostAddress::AnyIPv4, port);


Client QLocalSocket:

m_localSocket = new QLocalSocket();
m_localSocket->connectToServer("service name");
if (m_localSocket->waitForConnected(WAIT_FOR_CONNECTED_TIMEOUT)) {
    m_jsonRpcSocket = new QJsonRpcSocket(m_localSocket);
}


Client QTcpSocket:

m_tcpSocket = new QTcpSocket();
m_tcpSocket->connectToHost(m_rpcAddress, m_rpcPort);
if (m_tcpSocket->waitForConnected(WAIT_FOR_CONNECTED_TIMEOUT)) {
    m_jsonRpcSocket = new QJsonRpcSocket(m_tcpSocket);
}

Rpc is performed calling
pReplay = m_jsonRpcSocket->invokeRemoteMethod(.......)
for both versions.

It's happening that the local socket version works well, while the TCP version does not work when running the executables out of the IDE (qtcreator).
Running the code in qtcreator (debug mode) seems often working (not sure if it always works).
Using a network packet sniffer I noticed that the connection between client and server is estabilished, but rpc are not sent by the client (
invokeRemoteMethod does not produce data on the socket).
Any idea? What can I check? I'm pretty sure it could be an error in my code. Any other difference between local and tcp socket communication that I should take care?

best regards,
Marco

Matt Broadstone

unread,
Sep 4, 2015, 10:08:01 AM9/4/15
to Marco Vimercati, qjsonrpc-development
Hey Marco,

There should be no difference between using local sockets and tcp sockets other than the source used for the sockets (local file vs host address). Can you try running the manual "tcpserver" and "tcpclient" tests, and see if you have any issues there? If there is no issue there, then please provide a little more of your code so we have something to work with :)

Cheers,
Matt

 
best regards,
Marco

--
You received this message because you are subscribed to the Google Groups "qjsonrpc-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to qjsonrpc-develop...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marco Vimercati

unread,
Sep 7, 2015, 10:18:03 AM9/7/15
to Matt Broadstone, qjsonrpc-development
Dear Matt,
I tried tcp client/server tests you suggested, and obviously work.
Meanwhile I did some other tests on my code and discovered some things:
My RPC client code has a main body (thread) doing some stuff and another thread that makes use of the rpc calls.
The rpc client initialization code is placed an 'initialize' method of the second object/thread, which performs the rpc calls;
something like that:

Thread1:

do stuff
create second thread object  (obj2)
create second thread (t2)
obj2->moveToThread(t2)
obj2->initialize()   (this creates qtcpsocket and connects to the server).
t2->start()


Thread 2:
performs rpc calls, but actually does not send data on the socket.
Please note that I get this warning on the console:

QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

If I comment out the moveToThread the rpc starts working (but I obviously loose the second thread).

I don't understand the warning. Actually I'm doing all the configurations of the rpc client and invokes in the same thread.


best regards
Marco


Marco Vimercati

unread,
Sep 7, 2015, 11:25:23 AM9/7/15
to Matt Broadstone, qjsonrpc-development
Hi Matt,
few other pieces in my puzzle:
actually the rpc call are performed in the second thread, but not in a simple and clean way as we can hope :)
Due to the fact I need to call a remote method every x seconds (let's say a keepAlive method), I placed a QTimer in the 2nd thread:

code in SecondThread::initialize():
creates qtcpsocket and connects to the server

timer = new QTimer();    
connect(timer , &QTimer::timeout, this, &SecondThread::onTimeoutCallRPC);
timer ->start(frequency)

while SecondThread::onTimeoutCallRPC contains the RPC invoke methods.

I think this timer causes the problem, even thought it seems running in the same thread context.

Any idea?

Thanks,
Marco

Matt Broadstone

unread,
Sep 8, 2015, 7:56:55 AM9/8/15
to Marco Vimercati, qjsonrpc-development
Hey Marco,

Threading is a bit difficult because of the requirement for the qjsonrpc endpoint to return a value and the way Qt's sockets were designed.  You definitely cannot block the main/gui thread (or whatever thread you move the server to) if you want to ensure consistent functionality of the rpc server.

You might want to take a look at the "delayed response" tests, these allows you to "save" the current request until a later point (when you have completed processing something) when you chose to send the response back to the calling client.  This works quite well if you have a process that takes a little bit of time, or you want the server to better participate with the event loop in its current context (read: some QThread). 

A pattern I have used often when I need to use multiple threads is to pass that QJsonRpcServiceRequest object to a QRunnable subclass. During the QRunnable's exec method, I process some long-ish task, and at the end call `m_request.response(<some QJsonRpcMessage>)`. This way I can set up the processing, give it a request message and fire-and-forget on the thread pool. There are some examples in the tests, but I could probably dig up an easy example if that would help? Delayed responses are also good for use on a single thread, you might consider trying to go single-threaded as using multiple threads is often not required when writing a Qt application.  


As for the error you were seeing about QSocketNotifiers, try creating the server then calling "listen()" on it, and _then_ moving it to another thread. This is another tricky side-effect of Qt's socket code wrt threads, see an example of this in the qjsonrpcserver tests.

Matt

Marco Vimercati

unread,
Sep 8, 2015, 10:03:45 AM9/8/15
to Matt Broadstone, qjsonrpc-development
Yep, threading is a bit difficult, because these frameworks hide how things works under the hood and if you don't completly understand it the bugs are sure :) In fact I understood a bit more and solved my problem!
I had two kinds of problems:
1) I was not specifying the object relationship, passing the parent parameter to the objects constructors;
2) I was calling a method (initialize) of an object that was moved to another thread:


create second thread (t2)
obj2->moveToThread(t2)
obj2->initialize()   (this creates qtcpsocket and connects to the server).
t2->start()

so the objects (rpc clients and qtimers) created in 'initialize' had affinity with the main thread instead of the second one
(and this is very bad for qt tcp layer).

Moving the initialize before 'moveToThread' and setting the object hierarchy fixed the problem.

Thanks for the support
Bye Marco

Reply all
Reply to author
Forward
0 new messages