Fast and reliable COM-Tools for developers

dhRPCServer

This is the "flagship" in our Tool-Set and contains not only an XCopy-deployable Serverpart, it comes of course with an appropriate RPCClient-Part too.

The communication between the Client- and the Server-Part is done over TCP/IP-sockets under usage of a fast binary protocol (and one single, definable port only).

Basically it is the "barebone-version" of our larger dBAS-Applicationserver (mentioned here), optimized and tuned over the last years.

The current version of the RPC-Environment is 2.1.


Downloads:

Server-Binaries and Binaries for a COM- and a DB-DemoClient.

VB6-Sources for both of the Demo-Clients, mentioned above.


Here's a screenshot of the RPCServer, hosted on a Linux-machine and currently

"stressed" by two concurrent DB-Clients (one running on XP and one on Linux).


Features:

1.)

One goal of the developement was: simplicity (no mysterious DCOM/COM+

puzzling anymore, regarding SecurityConfig, UserRights, Registration).

Start RPCServer.exe (the RPC-Service-Controller) and from there start

dh_RPCService.exe (either as UserProcess or as Service).

Now start the ClientDemo (RPCDemoClient.exe) from inside the Client-

RPCDirectory - eh voilà - it should connect and work. Now test Cross-

Machine Calls in your LAN. Simply copy both, the Server- and Client-

Folder to the new Host, start the Server there, start the Client and

try to Cross-Connect from both Machines (using the appropriate HostIPs/

HostNames - it should work too. No MS-Management-Console, no DCOMConfig,

no Proxy-/Interface-Creation/Registration.

Just look at the Sources - The Code for wrapping basic SQLite-Read/Write-

functionality needs ca. 50 lines in the server-class (DBServer.Dll) and ca.

30 lines in the clientside Wrapper-Class. We have builtin support for (clientside)

Debugging of the whole RPC-Roundtrip - simply step through all of your Client-

and Server-side Code-Lines. As soon as your Server-Code is stable, you can

compile your Server.Dll and simply put it into the \RPCDlls-Folder in your

Server-Directory (no need to register anything), switch off DebugMode at the

Clientside and you can access your Dll at the "real server".


2.)

As already mentioned above, (and that's not only working in our Demo,

it will work for your ClientCalls and your ServerDlls too), there's no

need, to register Interfaces for our COM-Calls across Machine-Boundaries

(neither at the client-side, nor at the server-side) anymore - we use

LoadLibrary + LoadTypeLibEx + DllGetClassObject, to instantiate from

the Filesystem directly.

Wrapper-Classes at the client-side have to be written by hand, but

they need very small Implementation-Effort (look at the Demo-Source)

and can be Private Classes.


3.)

All Types, that are accepted by a (VB-)Variant-ParamArray can be transported

(the whole bunch of VBs "simple" Types and their Arrays).

Objects (altough not Object-Arrays) are supported too, as long as they

support the IPersistStream-Interface (most interesting in this regard

are ADO-Recordsets, wich allow this "Out-Of-The-Box").

No ANSI-Conversion is performed for String-Types, so we avoid problems

with different locales (the RPC-Requests offer full UniCode-Transport).

Full support for ByRef-Params - non-changed Byref-Params (especially

Arrays of simple Types and Recordsets) are detected by our Serializer

automatically, and do not waste bandwidth regarding their unnecessary

backtransport to the Client.


4.)

Fast transfers using an efficient binary Socket-Protocol, consuming only

one single Port (DCOM uses a complete Port-Range). Port 80 (http) can be

used also, because the server responses to http-Gets and -Posts correctly.

Our ZLib-based Compression-Feature becomes very usefull in Low-Bandwidth-

-Scenarios (Internet-Connections).

Strong-Encryption is also possible at Protocol-Level. We use Diffie-

Hellman-Authentication and avoid man-in-the-middle-attacks per Challenge/

Response using Public-/Private KeyPairs. After the DH-KeyExchange/Auth.

the serialized Packets are encrypted using 320Bit ArcFour-Encryption,

wich is very fast and generates not much Call-Overhead. But if you want

to work over your own VPNs or SSH-Channels, you can simply switch it off.

I've also tested RPCs succesfully in a Putty-initiated (Open-)SSH-Session

per DSL and all was working fine.


5.)

Authorization is possible at RPC-Level (or better: Connection-Level -

for RPC-Level-Impersonation-Switches you would have to reconnect before

each Call with another ServerKnown-Account). This feature allows, to use

server-side resources (NetWork-Shares, SQL-Servers) with the appropriate

Win-User-Rights for each Client-Connect (respective Request) independently.

The Server supports a ForceAuthentication-Switch, so that unauthorized

Connection-Attempts are blocked (disconnected instantly). It is also

possible, to restrict the Windows-Authentication to the Members of a self-

defined Group(name) only.


6.)

TimeOuts at Method-Call-Level. TimeOuts are handled properly, even if

the bandwidth is limited (on slower internet-connections there will be

a packetbased "TimeOut-Retriggering"), so that you can define your TOuts

very narrow to the duration of the pure server-side Method-Call, no need

to take latencies or slow transfers due to low bandwidth into respect.


7.)

The Server is storing requests in a Job-Queue and gives them from there

to a WorkerThread-Pool, wich finally does the processing. The PoolSize

can be changed dynamically whilst the server is running. The Server hardly

terminates a running request (on its WorkerThread) after RPC-Timeout + 0.5

sec without losing stability, a terminated Thread is replaced with a fresh

WorkerThread-Instance.


8.)

Stateful Objects: Altough Objects in the "Server-Layer" normally should

be called in a stateless manner, there are often scenarios, where you

want to "pin" values between requests (complex transactions come to mind,

or the requirements of enhanced session-management), or you simply want to

let a serverside resource opened between the calls (e.g. COM-Ports, etc.).

For this purpose we allow to instantiate two types of Singletons - public

reachable Singletons (X-Thread and X-Process over the ROT per GetObject)

and RPC-internal Singletons, not visible to the outside. These Objects can

be created from your own server-side DLL-Classes. They are instantiated on

separate threads beside the WorkerThread-Pool (also done without using the

registry - as usual here). They allow marshaled "In-Call-Order-Access"

from the "normal" RPCs (wich run on their own WorkerThreads). The COM-

Marshaling takes care, that parallel requests from different WorkerThreads

are queued. But look at our Demo, you will find commented examples there.


************************** Limitations ***************************

Max Client-Connects: 1024

Max ThreadPool-Size: 64

Max Client-Req-Size: 2,000,000 Bytes (see comments in the COM-Demo)

Max Servr-Resp-Size: 4,000,000 Bytes (see comments in the COM-Demo)


Performance:

Performance is very good, the server can process around 5000 small COM-

Requests per second. (measured under heavy load on a "normal" PIV 3.0GHz

Machine without HyperThreading, using our Demo-Client and its String-

Reflection-Loop from multiple ClientMachines in a 100MBit-LAN).

The SMP-Values for a AthlonX2 (2x2000MHz) look even better of course:

7000 simple ComReq/s with a ProcessorLoad of 50% (10000 with 95% Load).

Performance for a single connected Client is around 500 COMReq/sec, so a

simple Calls Roundtrip-Time calculates (as a consequence) to around 2ms.


Combined with dhSQLite (look at the Demos from the Download-Links above) the
Server reaches outstanding Multi-User-Results.
You can expect ca. 120 Responses/sec, serving 'Select * from Orders' or ca. 500 Responses/sec serving 'Select * from Customers' - stresstested from multiple Clients against a "Standard-NWind.db' on a simple Dual-Athlon-CPU (2GHz - SATA 7200/min,
512 MB Ram, 100MBit-LAN, Protocol-Compression and -Encryption enabled).


One additional note:

I've just tested the whole thing (DemoClient-App and Server) on an actual

Linux-Box (Debian-based) with the current Wine-Version 0.935, and there's

absolutely nothing (regarding the RPC-Calls), that doesn't work as expected

(no Server-Crashes, no Memory-Leaking, nothing) - even the ADO-Recordset-

Calls finished succesfully (tested Linux->Linux and XP->Linux scenarios).

So hosting your '*.mdb', your SQLite-DBs (the Server works best with SQLite)

or a "real" SQL-Server like PostGres (wich has its own OleDB-ADOProvider -

Firebird also) together with the server-side Logic on an inexpensive

Linux-based Internet-Hoster becomes possible now.

Linux was faster for Remote-Calls with large Content (not much, but

faster ;-) - probably due to the very good Wine-Translation into the

Linux-Socket-Stack). As expected, it has larger COM-Overhead regarding

Class-Instantiation and Method-Invoking-ByName - so our Stressing-Loop

from different DemoClients (small Com-Calls with very few Data)

sumed up to around 3500 Server-Responses per second under heavy load (where

the XP-Box had 10000). But the more realistic ADO-Recordset-Call was finished

after 8msec (XP 7ms) - so this relativates the Call-Overhead (factor 3) to

some factor 1.2 for usual (DB-)calls.

Despite the COM-Overhead, these are very good values - I was impressed.

Thanks to the Wine-folks, who made this possible!