DCOM: How to close the connection on the server when the client fails?

I have a rather old project: DCOM client and server, both in C ++ \ ATL and on the Windows platform. Everything works fine: local and remote clients connect to the server and work simultaneously without any problems.

But when the remote client crashes or is killed by the task manager or the "taskkill" command or by turning off the power, I have a problem. My server does not know anything about a client failure and is trying to send new events to all clients (also to an already broken one). As a result, I have a pause (the server cannot send data to an already broken client), and its duration is proportional to the number of broken remote clients. After 5 broken clients, the pauses are so large that they are equal to the server stop completely.

I know about the DCM ping processor (DCOM should disconnect clients that do not respond to "every 2 minutes ping" after 6 minutes of silence). Indeed, after 6 minutes of freezing, I have a short period of normal operation, but then the server returns to the "suspended" state.

What can I do with all this? How to make DCOM "ping" great? If I can implement my own ping code, is it possible to manually disconnect the connection of old DCOM clients? How to do it?

+6
c ++ atl dcom
source share
4 answers

I'm not sure about DCOM's ping system, but one option for you is to simply turn off notifications to a separate thread pool. This will help mitigate the effect of having a small number of blocking clients - you will have problems if there are too many of them, of course.

An easy way to do this is to use QueueUserWorkItem - this will call the passed callback in the application thread pool. Assuming you are using MTA, this is all you need to do:

 static InfoStruct { IRemoteHost *pRemote; BSTR someData; }; static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) { CoInitializeEx(COINIT_MULTITHREADED); InfoStruct *is = (InfoStruct *)lpInfo; is->pRemote->notify(someData); is->pRemote->Release(); SysFreeString(is->someData); delete is; CoUninitialize(); return 0; } void InvokeClient(IRemoteHost *pRemote, BSTR someData) { InfoStruct *is = new InfoStruct; is->pRemote = pRemote; pRemote->AddRef(); is->someData = SysAllocString(someData); QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION); } 

If your main thread is in an STA, this is a little more complicated; you just need to use CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream to pass the interface pointer between the apartments:

 static InfoStruct { IStream *pMarshalledRemote; BSTR someData; }; static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) { CoInitializeEx(COINIT_MULTITHREADED); // can be STA as well InfoStruct *is = (InfoStruct *)lpInfo; IRemoteHost *pRemote; CoGetInterfaceAndReleaseStream(is->pMarshalledRemote, __uuidof(IRemoteHost), (LPVOID *)&pRemote); pRemote->notify(someData); pRemote->Release(); SysFreeString(is->someData); delete is; CoUninitialize(); return 0; } void InvokeClient(IRemoteHost *pRemote, BSTR someData) { InfoStruct *is = new InfoStruct; CoMarshalInterThreadInterfaceInStream(__uuidof(IRemoteHost), pRemote, &is->pMarshalledRemote); is->someData = SysAllocString(someData); QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION); } 

Please note that error checking has been eliminated for clarity - you, of course, want the error to check all calls - in particular, you want to check for RPC_S_SERVER_UNAVAILABLE and other similar network errors and remove offensive clients.

Some of the more complex options that you might want to consider include providing only one in-flight request per client at a time (thus further reducing the impact of a stuck client) and caching the MTA marshalling interface pointer (if your main thread - it STA) - as I believe that CoMarshalInterThreadInterfaceInStream can perform network requests, you ideally want to take care of it ahead of time, when you know that the client is connected, rather than risking your main lock on stream.

+1
source share

One solution would be to rule out events - force clients to request a server to see if there is anything interesting.

0
source share

Use DCOM to set the name of the named pipe. Disconnecting is better with pipes. The listener answers (almost) instantly to messages. e.g. Server-> Client (what is your pipe name?). Client-> Server responds with a name that includes the machine. The client creates a named pipe and listens. The server opens the handset immediately or as needed.

0
source share

You can implement your own ping mechanism so that your clients call the server ping method from time to time. You already support some kind of container for your clients on the server side. In this map, mark each client with the label of the last ping. Then check if the client is alive before sending events to this client. You can set up a strategy when to stop sending events, possibly based on the time or the number of pings skipped or the type of event or some other factors. You probably don't need to worry about deleting clients that may wait until DCOM realizes that a particular client is dead. This scheme cannot completely fix the problem, since the client may die just before the event is sent, but you will have full control over how many such clients can exist by setting the ping period. The shorter this period, the less dead customers, although you pay for traffic.

0
source share

All Articles