6/24/2008

the RPC Way to Interop With Remote Process

When we say "interop with remote process", people may think "oh, its just remote ipc, which is actually network communication". Yes, it's network communication, but not that easy as it may seem at the first sight.

Besides raw socket mechanism, there are many well defined styles in which a process can communicate with remote processes: Named Pipe, Mailslot are those that are available on Windows platform.

In this article, I will focus on another specific remote IPC mechanism: Remote Procedure Call - communicating with remote process as method call.

1. Why people invent RPC?

When physical network communication comes true in computer world, software designer starts to think how to provide a software interface to these great facility.

Raw socket abstraction is the first step, it is powerful but too low-level - you see connection/listen/accept/byte stream. Networking is for remote IPC, but remote IPC semantic is far more rich than just connection and raw byte stream.

So here comes the invention of RPC. According to the earliest paper, the main motivation behind the design of RPC is - procedure call is a well-known and well-understood mechanism/semantic, making it work among remote computers will make distributed system easier to be built and get right.

2. What problems should RPC solve?

Key problems:

-1]. Binding Remote Methods(A.K.A. identifying/naming/addressing/locating). It concerns with two fundamental problems: how to let caller specify which callee to communicate (Naming)? how to let caller find the physical address(network address/port/process ID etc) of the callee (Locating)?

-2]. Data Unmarshall/Marshall. It concerns how caller encode its data types (in some programming language) into byte stream and how callee decode byte stream into its desired data types (may be in another programming language on another platform). It is typically called Message Encoding. More recently, there is also a related problem - Message Formatting, which define how the message between client/server is layoutted.

-3]. Message Transporting. It concerns how to send/receive data stream, for example, how to manage connection, congestion, failure and time out etc.

Every RPC mechanism has specific solution to these problems.

Basic Components for RPC system:
- Client Code
- Client Stub
- RPC runtime (at both client and server side)
- Server Stub
- Server Code

3. What are the popular RPC system today?

Function Oriented - classic RPC
- Distributed Computing Environment by HP, IBM, DEC and others
- Open Network Computing by SUN
- MSRPC by Microsoft, it's a modification to DCE/RPC and MSRPC is widely used in DCOM, which could be regarded as MSRPC + COM.

Object Oriented - OO RPC
- .Net Remoting by Microsoft
- Java Remote Method Invocation by Sun
- DCOM: Distributed COM by Microsoft
- COBRA: Common Object Request Broker Architecture by Object Management Group
- Jini - Advanced Java RMI by Sun

HTTP/XML Oriented - Web Service
- XML-RPC, xml as data representation and http as transport channel
- JSON-RPC, JSON as data representation, http/socket as transport channel
- SOAP WS, (A.K.A big web service) xml with strong schema as data/message representation, http/smtp/tcp etc as transport layer, security/transaction related protocols (so called WS-*) are added to support enterprise application
- RESTful WS, xml/json/...(flexible) as data encoding, simple & flexible message format, http as transport channel, http method & uri to represent action and stateless in communication

4. Other stuff that matters

The Extensive Adoption of XML

Advantages of using XML
- software that marshall to, unmarshall from xml is easy to get
- text based data representing, easy to read(but why should we read those data?)
- it's flexible and easy to extend

Disadvantages of using XML
- high computing/memory cost
- high cost of communication

The Extensive Adoption of HTTP

- In fact, the HTTP is not a general purpose communication facility by design. The initial motivation to adopt HTTP as transport channel in web service protocols is to communicate through Internet firewalls.

Many criticisms say that HTTP/XML is abused in today's web-scale distributed systems. Yes, these technologies are used in a way other than the initial target, but very few innovations are created as planned, rather, many great inventions are just happy accidents. HTTP/XML may not be the best technologies, but they do be the most successful ones in web world.

Windows Communication Foundation

WCF by no means invents any new communication protocol. It is just a programming framework that implements existing protocols on .Net platform. The main contribution of WCF is that it uniforms various communication protocols(SOAP, WSE, .Net Remoting, Message Queue, JSON-RPC and REST etc.) into one programming model in .Net framework.

[Reference]

Remote Procedure Call
Wiki about RPC - http://en.wikipedia.org/wiki/Remote_procedure_call
RFC about RPC - http://tools.ietf.org/html/rfc707
RPC Tutorial - http://www.cs.cf.ac.uk/Dave/C/node33.html
RPC Implementation - http://pages.cs.wisc.edu/~cs736-1/papers/rpc.pdf
DCE RPC - http://www.opengroup.org/dce/
SUN RPC - http://www.onc-rpc-xdr.com/
MS RPC - http://msdn.microsoft.com/en-us/library/aa378651(VS.85).aspx

Web Service
The SOAP/XML-RPC/REST Saga, http://www.tbray.org/ongoing/When/200x/2003/05/12/SoapAgain
History of SOAP, http://webservices.xml.com/pub/a/ws/2001/04/04/soap.html
XML-RPC, http://www.xmlrpc.com
SOAP Tutorial, http://www.w3schools.com/soap/default.asp
Build WS the REST way, http://www.xfront.com/REST-Web-Services.html

6/10/2008

Windows Service - How To

Part I - Why to invent so called "Windows Service"?
1. can automatically started when the computer boots, can be paused and restarted
2. can run in their own windows sessions, and do not show any user interface(no keyboard, mouse and monitor needed, background process)
3. similar to "Daemon" in *nix world

Part II - Windows Service Components
1. Service Database
- register based data store
- store configuration/attribute of each service

2. Service Control Manager
- a RPC server started at system boot
- manage service database
- control individual services directly

3. Individual Service
- Service Program, A program that provides executable code for one or more services
- Service Configuration Program, A program that queries or modifies the services database(install/delete/modify service programs)
- Service Control Program, A program that starts and controls services and driver services
- all the upper 3 prgms leverage functionalities from SCM

Part III - Steps to develop a Windows Service
1. composing service program, which includes:
- main function, called by SCM. It should start the service dispatch loop/thread and execute the servicemain function for each service it contains.
- servicemain function, it contains service specific logic.
- control handler, defines how the service serves the control messages from SCM.
- the thread model is "1 + N": 1 thread for control dispatching and N for each servicemain.

2. composing service configuration/control program
- can be in a shared/individual executable
- config program interact with Windows Service in a static way, while control program in a dynamic way
- both talks to Windows Service indirectly using SCM as the mediator

Part IV - How to debug Windows Service:

1. Use your debugger to debug the service while it is running. First, obtain the process identifier (PID) of the service process. After you have obtained the PID, attach to the running process. For syntax information, see the documentation included with your debugger.

2. Call the DebugBreak function to invoke the debugger for just-in-time debugging.
Specify a debugger to use when starting a program. To do so, create a key called Image File Execution Options in the following registry location:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

3. Use Event Tracing to log information.

4. Write the service as console application first. After debugging/verification, converted it to Windows Service form.

Let's see an code example of a service, who writes an event record to Windows periodically.
Code Snippet I - Service Program
  1 #include <windows.h>
  2 #include <stdio.h>
  3 #include "msg.h"
  4
  5 BOOL g_isShutdown = FALSE;
  6 BOOL g_isPaused = FALSE;
  7
  8 SERVICE_STATUS g_servStat = {0};
  9 SERVICE_STATUS_HANDLE g_hServStat = NULL;
 10
 11 DWORD WINAPI TimerServiceHandler(DWORD  dwControl, DWORD  dwEventType, LPVOID lpEventData, LPVOID lpContext)
 12 {
 13     switch (dwControl)
 14     {
 15     case SERVICE_CONTROL_CONTINUE:
 16         g_isPaused = FALSE;
 17         break;
 18
 19     case SERVICE_CONTROL_PAUSE:
 20         g_isPaused = TRUE;
 21         break;
 22
 23     case SERVICE_CONTROL_SHUTDOWN:
 24         g_isShutdown = TRUE;
 25         g_servStat.dwCurrentState = SERVICE_STOPPED;
 26         break;
 27
 28     case SERVICE_CONTROL_STOP:
 29         g_isShutdown = TRUE;
 30         g_servStat.dwCurrentState = SERVICE_STOPPED;
 31         break;
 32
 33     default:
 34         break;
 35     }
 36
 37     SetServiceStatus(g_hServStat, &g_servStat);
 38
 39     return 0;
 40 }
 41
 42 // ServiceMain of Service Program
 43 void WINAPI TimerServiceMain(DWORD argc, LPWSTR *argv)
 44 {
 45     // Register service control handler
 46     SERVICE_STATUS_HANDLE g_hServStat = RegisterServiceCtrlHandlerEx(L"MyTimer", TimerServiceHandler, NULL);
 47
 48     // Set service initial status
 49     g_servStat.dwCheckPoint = 0;
 50     g_servStat.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
 51     g_servStat.dwCurrentState = SERVICE_START_PENDING;
 52     g_servStat.dwServiceSpecificExitCode = 0;
 53     g_servStat.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 54     g_servStat.dwWaitHint = 2 * 1024;
 55     g_servStat.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
 56     if (!SetServiceStatus(g_hServStat, &g_servStat))
 57     {
 58         // report error in the way you like: disk file, windows event
 59         printf("failed to set service init status\n");
 60     }
 61
 62     // Your service specific logic here
 63     HANDLE hEvtSrc = RegisterEventSource(NULL, L"MyTimerService");
 64     if (hEvtSrc == NULL)
 65     {
 66         // use your own service info reporting mechanism
 67         printf("Cannot register the event source.");
 68         return;
 69     }
 70
 71     // Init done, report new service status to SCM
 72     g_servStat.dwCurrentState = SERVICE_RUNNING;
 73     SetServiceStatus(g_hServStat, &g_servStat);
 74
 75     wchar_t buf[MAX_PATH];
 76     LPCWSTR lpBuf = buf;
 77     UINT32 uiCounter = 0;
 78     while (!g_isShutdown)
 79     {
 80         if (!g_isPaused)
 81         {
 82             // see windows event documentation for how to use it in your own program
 83             swprintf_s(buf, MAX_PATH, L"Info from Gate Keeper Service: %u\n", uiCounter++);
 84             ReportEvent(hEvtSrc, EVENTLOG_SUCCESS, NULL, MSG_INFO_COMMAND, NULL, 1, NULL, &lpBuf, NULL);
 85         }
 86         Sleep(5 * 1000);
 87     }
 88    
 89     DeregisterEventSource(hEvtSrc);
 90
 91     g_servStat.dwCurrentState = SERVICE_STOPPED;
 92     SetServiceStatus(g_hServStat, &g_servStat);
 93
 94     return;
 95 }
 96
 97 // The Main Function of Service Program
 98 int main(int argc, char** argv)
 99 {
100     SERVICE_TABLE_ENTRY dispatchTable[] =
101     {
102         {L"MyTimer", TimerServiceMain},
103         {NULL, NULL}
104     };
105
106     // Register ServiceMain to Service Control Manager
107     if (!StartServiceCtrlDispatcher(dispatchTable))
108     {
109         // you can choose to write the info msg to disk file or windows event
110         printf("Failed to register service main to SCM.Please check system logs\n");
111         return -1;
112     }
113
114     return 0;
115 }
116

Code Snippet II - Service Configuration/Control Program

 1 #include <windows.h>
 2 #include <stdio.h>
 3
 4 int wmain(int argc, wchar_t** argv)
 5 {
 6     SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 7     if (hSCM == NULL)
 8     {
 9         printf("failed to open sc manager, due to error:%d\n", GetLastError());
10     }
11
12     // Service Configuration Program - Install a new Windows Service
13     SC_HANDLE hServ = CreateService(hSCM, L"MyTimer",
14             L"Time Service",
15             SC_MANAGER_ALL_ACCESS,
16             SERVICE_WIN32_OWN_PROCESS,
17             SERVICE_DEMAND_START,
18             SERVICE_ERROR_NORMAL,
19             L"D:\\Dev\\Debug\MyTimer.exe",
20             NULL,
21             NULL,
22             NULL,
23             NULL,
24             NULL);
25     if (hServ == NULL)
26     {
27         printf("create windows service failed, due to error:%d\n", GetLastError());
28     }
29
30     // Service Configuration Program - Delete an existing Windows Service
31     //SC_HANDLE hServ = OpenService(hSCM, L"MyTimer", SERVICE_ALL_ACCESS);
32     //if (!DeleteService(hServ))
33     //{
34     //  printf("delete windows service failed, due to error:%d\n", GetLastError());
35     //}
36
37     // Service Control Program - Start/Pause/Resume/Stop the Windows Service
38     if (!StartService(hServ, argc, const_cast<LPCWSTR*>(argv)))
39     {
40         printf("failed to start service due to error:%d", GetLastError());
41     }
42
43     SERVICE_STATUS servStat;
44     QueryServiceStatus(hServ, &servStat);
45
46     if (!ControlService(hServ, SERVICE_CONTROL_PAUSE, &servStat))
47     {
48         printf("failed to control service due to:%d\n", GetLastError());
49     }
50
51     if (!ControlService(hServ, SERVICE_CONTROL_CONTINUE, &servStat))
52     {
53         printf("failed to control service due to:%d\n", GetLastError());
54     }
55
56     if (!ControlService(hServ, SERVICE_CONTROL_STOP, &servStat))
57     {
58         printf("failed to control service due to:%d\n", GetLastError());
59     }
60
61 }
62

Note:
1. You can see the main/servicemain/controlhandler of Service Program from the code comments.
2. You can also see the Service Configuration/Control Program from the second code snippet. These two programs are merged in one executable.
3. You should update service status each time a control command is processed in your control handler function, even if the status is not changed at all, as illustrated in line 37@code snippet I. This will let SCM aware that your service is in progress, not hang.
4. Windows Service applications run in a different window station than the interactive station of the logged-on user. The Windows service station is not interactive, so dialog boxes raised from within a Windows service application will not be seen and may cause your program to stop responding. Error/Debug messages should be logged in the Windows event log.

full source code package:
http://code4cs.googlecode.com/files/NotifyService4W.zip