qtrpc2 - - qt developer days 2014 - home | qt …...2 about me • co-founded resara llc (2004-2012)...
TRANSCRIPT
1
www.ics.com
QtRpc2
A Qt Based RPC LibraryBy: Brendan Powers
2
www.ics.com
About Me
• Co-Founded Resara LLC (2004-2012)
• Linux infrastructure products
• Heavy use of Qt
• Used Qt’s core classes for back end code.
• Highly distributed applications
• Qt Software Engineer at ICS (2012-Present)
3
www.ics.com
About Integrated Computer Solutions
• ICS is the largest Qt professional services firm in North America.
• ICS provides embedded, mobile, and desktop consulting services for Qt, multi-touch applications and GUI application development.
• As a Qt-Certified Training Partner, ICS has trained thousands of Qt developers.
• Visit www.ics.com for more info and free training videos.
4
www.ics.com
The Problem
• Many applications spread among many servers
• Applications needed access to many different services
• Existing C++ RPC libraries were complicated and verbose
• A need for a flexible RPC framework, without increasing code complexity
5
www.ics.com
The Solution – QtRpc2
• Ease of use is the first priority
• Uses Qt’s meta object system to simplify RPC calls
• Write services like you would create any QObject
• Remote functions act like normal C++ functions
• Wait, what about QtRpc 1?
• Used service files to generate templates
• Error handling was awkward
• Custom data types were difficult to implement
6
www.ics.com
Features
• Simple to use
• Events and asynchronous function calls
• Simple error checking
• Token based authentication model
• Multiple transport modes (TCP, SSL, Named Pipe)
• Flexible threading model
• Service discovery
7
www.ics.com
Where to get Qtrpc2
• Git repositoryhttps://github.com/brendan0powers/QtRpc2
• Ubuntu packages (Ubuntu 12.04 and up)apt-get install libqtrpc2-dev
• Windows binarieshttps://github.com/brendan0powers/QtRpc2/downloads
8
www.ics.com
Architecture - Services
ProxyBase
ClientProxy ServiceProxy
9
www.ics.com
Architecture – Server
Server ServiceProxy
Tcp Listener
ServiceProxy
ServiceProxy
Socket Listener
registerService()
Tcp Listener
Socket Listener
Tcp Listener
Socket Listener
…
10
www.ics.com
Architecture – Client
ClientProxy
11
www.ics.com
Basic Example - Server
class BasicService : public ServiceProxy {
Q_OBJECT
public:
explicit BasicService(QObject *parent = 0) {}
virtual ReturnValue auth(QString user, QString pass) {
return(true);
}
public slots:
ReturnValue addNumbers(int a, int b) {
return(a + b);
}
};
12
www.ics.com
Basic Example - Server
int main(int argc, char *argv[]) {
QApplication app(argc,argv);
Server srv;
ServerProtocolListenerTcp tcp(&srv);
if(!tcp.listen(QHostAddress::Any, 10123)) {
qCritical() << "Failed to listen on port 10123!";
return(1);
}
srv.registerService<BasicService>("MyService");
return app.exec();
}
13
www.ics.com
Basic Example - Client
class BasicService : public ClientProxy {
Q_OBJECT
QTRPC_CLIENTPROXY(BasicService)
public:
explicit BasicService(QObject *parent = 0) {}
signals:
ReturnValue addNumbers(int a, int b);
};
14
www.ics.com
Basic Example - Clientint main(int argc, char *argv[]) {
QApplication app(argc,argv);
BasicService service;
ReturnValue ret = service.connect("tcp://localhost:10123/MyService");
if(ret.isError()) {
qCritical() << "Failed to connect:" << ret;
return(1);
}
ret = service.addNumbers(3,5);
if(ret.isError()) {
qCritical() << "Failed to call addNumbers():" << ret;
return(1);
}
}
15
www.ics.com
ReturnValue
• Almost always used as the return type in QtRpc2 functions
• Inherits from QVariant
• Used for error checking
• Errors stored as an error number, and error message
• Important functions
• isError()
• errNumber()
• errString()
16
www.ics.com
ReturnValue
• ReturnValue(5);
• ReturnValue(“Some Text”);
• ReturnValue(123, “This is an error!”);
• ReturnValue = QVariant::fromValue(CustomType);
• ReturnValue.value<CustomType>();
17
www.ics.com
Events
• Behave exactly like Qt’s signals
• Can be connected to other signals, or slots
• Signified by the Event return type.
class TimeService : public ClientProxy {
Q_OBJECT
QTRPC_CLIENTPROXY(TimeService)
public:
explicit TimeService(QObject *parent = 0);
signals:
Event currentTime(QDateTime time);
};
18
www.ics.com
Events - Client
QObject::connect(&service, SIGNAL(currentTime(QDateTime)), &object, SLOT(currentTime(QDateTime)));
void TimeObject::currentTime(QDateTime time) {
qDebug() << "Current Time:" << time.toString();
}
19
www.ics.com
Events - Server
class TimeService : public ServiceProxy {
Q_OBJECT
public:
explicit TimeService(QObject *parent = 0);
virtual ReturnValue auth(QString user, QString pass);
public slots:
void timeout();
signals:
Event currentTime(QDateTime time);
private:
QTimer *m_timer;
};
20
www.ics.com
Events - Server
TimeService::TimeService(QObject *parent) : ServiceProxy(parent) {
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
}
ReturnValue TimeService::auth(QString user, QString pass) {
m_timer->start(1000);
return(true);
}
void TimeService::timeout() {
emit(currentTime(QDateTime::currentDateTime()));
}
21
www.ics.com
Asynchronous Function Calls
• Normal function calls block until the server returns
• Asynchronous function calls call a slot when the server returns
• Add “QObject *, const char *” to any ClientProxy function to make is async
• the slot signature is (uint id, ReturnValue ret)
ReturnValue pause();ReturnValue pause(QObject *object, const char *slot);
ret = service.pause(&object, SLOT(pause(uint,ReturnValue)));
void TestObject::pause(uint id, ReturnValue ret) {if(ret.isError())
qDebug() << "Async Pause returned with an error:" << ret; else
qDebug() << "Async Pause returned.";}
22
www.ics.com
Authentication
• Simple User/Password based authentication
• auth(Qstring user, QString pass)
• Return true, or any other value for success
• Return an error for failure
• Authentication token based authentication
• auth(AuthToken token)
• Supports more data than just username and password
• Can store server side data about the users session and authentication state
• Use the authToken() function in a service to get the current users token
23
www.ics.com
Authentication - AuthToken
• Stores data in key/value pairs
• Client and Server data
• Client Data
• Data provided by the client for authentication
• Can be modified by the client
• Do not store session information in the client data
• Server Data
• Stores session information
• Cannot be modified by the client
24
www.ics.com
Authentication - Server
ReturnValue BasicService::auth(QString user, QString pass) {
if((user == "joe") && (pass == "secret"))
return(true);
else
return(ReturnValue(1,"Incorrect username or password."));
}
25
www.ics.com
Authentication - Client
ret = service.connect("tcp://jon:secret@localhost:10123/MyService");
if(ret.isError()) {
qCritical() << "Failed to connect:" << ret; return(1);
}
26
www.ics.com
Transports
• QtRpc2 comes with several transports.
• TCP – tcp://
• ServerProtocolListenerTcp
• Basic transport for network communications
• SSL – tcps://
• ServerProtocolListenerTcp
• Encrypted TCP transport.
• You must set a valid private certificate on the server before use
27
www.ics.com
Transports
• Socket – socket://
• ServerProtocolListenerSocket
• Uses sockets on UNIX systems, named pipes on Windows systems
• On Linux, you can get the UID and GID of the connecting user
28
www.ics.com
SSL Example - Server
Server srv;
ServerProtocolListenerTcp tcp(&srv);
tcp.setSslMode(ServerProtocolListenerTcp::SslForced);
tcp.setCertificate("./examplecert.pem");
if(!tcp.listen(QHostAddress::Any, 10123)) {
qCritical() << "Failed to listen on port 10123!";
return(1);
}
srv.registerService<BasicService>("MyService");
29
www.ics.com
SSL Example - Client
BasicService service;
ReturnValue ret = service.connect("tcps://localhost:10123/MyService");
if(ret.isError()) {
qCritical() << "Failed to connect:" << ret;
return(1);
}
30
www.ics.com
Threading• QtRpc2 supports multiple threading modes
• Single Threaded
• All services are created in the main thread
• One service can block all others
Server srv(NULL, Server::SingleThread);
• Thread Per Instance
• A new thread is created for each service instance
• Services to not block each other
Server srv(NULL, Server::ThreadPerInstance);
31
www.ics.com
Threading• Thread Pool
• Services are assigned to one thread in a thread pool
• Services can block other services using the same thread in the pool
• The default constructor uses thread pool mode, where the number of threads matches the number of cores
• You can specify the number of threads in the pool
//Thread pool mode. Threads = number of cores.Server srv;
//Thread pool mode. Threads = 3 Server srv(NULL, Server::ThreadPool, 3);
32
www.ics.com
Threading – Client Side• All networking happens in a separate message bus thread
• Asynchronous function calls are not blocked by network delays
• ClientProxy object may only be used by one thread at a time
• If needed, a ClientProxy can be copied and used in 2 threads simultaneously
BasicService service; //Use in thread 1
ReturnValue ret = service.connect("tcp://localhost:10123/MyService");
if(ret.isError()) {
qCritical() << "Failed to connect:" <<
ret; return(1);
}
BasicService service2(service); //Use in thread 2
33
www.ics.com
Custom Data Types
• Register your data type with the Qt meta-object system
• Create QDataStream operators
• Use QVariant::fromValue() and ReturnValue.value() to pack and unpack your data type
struct CustomData {int x; int y; int z;
};QDataStream& operator<<(QDataStream& d, const DataService::CustomData& object); QDataStream& operator>>(QDataStream& d, DataService::CustomData& object);Q_DECLARE_METATYPE(DataService::CustomData)
34
www.ics.com
Custom Data Types
QTRPC_REGISTER_METATYPE(DataService::CustomData)
QDataStream& operator<<(QDataStream& d, const DataService::CustomData& object) {
d << object.x;
d << object.y;
d << object.z;
return(d);
}
QDataStream& operator>>(QDataStream& d, DataService::CustomData& object) {
d >> object.x;
d >> object.y;
d >> object.z;
return(d);
35
www.ics.com
Returning Services From Functions
• Service can be returned from functions
• QtRpc2 takes ownership of the service object
• Service objects can be returned to more than one client at a time
ReturnValue DataService::getBasicService() {
return(new BasicService());
}
ReturnValue ret = service.getBasicService();
BasicService basic = ret;ret = basic.addNumbers(3,5);
36
www.ics.com
Service Discovery
• Allows clients to find servers on the network without knowing their address ahead of time
• Uses the MDNS or App’s Bonjour protocol.
• Platform Requirements
• Windows – Bonjour SDK and Bonjour Printing Runtime.
• Linux – Avahi with MDNS compat libs
• Mac – Bonjour libraries provided by the Apple SDK
37
www.ics.com
Service Discovery - Server
ServiceProxy *service = srv.registerService<BasicService>("MyService");
ServicePublisher pub(service);
pub.setPort(10123);
pub.publish();
38
www.ics.com
Service Discovery - Client
TestObject::TestObject(QObject *parent) : QObject(parent) {
ServiceFinder *finder = new ServiceFinder("MyService",this);
QObject::connect(finder, SIGNAL(serviceEvent(ServiceFinder::Service)), this, SLOT(serviceEvent(ServiceFinder::Service)));
finder->scan(); }
void TestObject::serviceEvent(ServiceFinder::Service service) {
if(service.port() == 0)
return;
QString host = service.address().toString();
int port = service.port();
QString url = QString("tcp://%1:%2/MyService").arg(host).arg(port);
39
www.ics.com
Room For Improvement
• Better documentation
• Templated ReturnValue class
• Non binary protocol option
• Packages for other Linux distributions
40
www.ics.com
Questions?