Client/Server interprocess communication via Shared Memory

Win32 offers several possibilities for interprocess communication (shared memory, mailslots, named pipes to name a few). Each has its advantages and disadvantages - the best one does not actually exist. This article describes how to use shared memory for interprocess communication in the following scenario:
  • Multiple processes are communicating to one process (kind of similar to client/server architecture on a local machine).
  • Data transfer is bidirectional which means that each process (client) sends data to the server and collects an answer.
  • Data transfer is initiated by the clients.
  • Access to shared memory must be protected from concurrent access which results in data corruption - synchronization.

Note: Shared memory is a very convenient method for interprocess communication on a local machine since it will work both on Windows 95 and on Windows NT. Named Pipes (better choice since it works also via a network) are not fully supported on Windows 95.

Since multiple clients are communicating to the server, it is assumed that the server proccess must be started first. However, this is not a requirement. First, I will describe the server side and then the client side.

Server

Server should create (during initialization) a named file mapping object whose size is defined by the project requirement. Name of the file mapping object should not be something obvious otherwise there is a possibility that it already exists. Also, take care what happens if it is allowed to start multiple instances of the server. For the purpose of this article, it is assumed that only one instance of the server is allowed to be started at any time. After creating a name file mapping object, server should map a view to this object in order to obtain a pointer to the shared memory for further use.

struct TSharedMemory {
    // Programmer defined structure (project specific)
};
TSharedMemory *Msg;

HANDLE hmem = ::CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,

                                    sizeof(TSharedMemory),"ApplicationSpecificFileMappingName");
Msg = (TSharedMemory*)::MapViewOfFile(hmem,FILE_MAP_WRITE,0,0,sizeof(TSharedMemory));

Besides shared memory, server must also create two named event objects. These events are used by the client to trigger the server to process client data (HExec) and by the server to indicate to the client that data are processed and answer is ready for the client (HDone).

HANDLE HExec = ::CreateEvent(NULL,FALSE,FALSE,"ApplicationSpecificEventExec");
HANDLE HDone = ::CreateEvent(NULL,FALSE,FALSE,"ApplicationSpecificEventDone");

Server should allocate a separate background thread for communication with the clients. Thread executive function should be (pseudo-code):

while (TRUE) {
    // Timeout is 1 second but can be anything else
    retcode = ::WaitForMultipleObjects(HExec and local event EKillThread, 1000);
    if (retcode is WAIT_TIMEOUT) {
        // Handle timeout if needed
    } else if (retcode is EKillThread) {
        // Kill thread
        break;
    } else if (retcode is HExec) {
        // Client signalled that data in shared memory is ready
        // Handle data, set answer to the client (all via Msg pointer to shared memory)
        ::SetEvent(HDone);
    }
};

Basically, thread is waiting for the event HExec to be signalled (this is done by the client after the shared memory is filled with data). Server processes data, places its own answer into the shared memory and triggers the client by signalling the event HDone.

Client

Client is started after the server. During initialization, client should open a file mapping object created by the server and map a view to this object to obtain a pointer for local use:

HANDLE hmem = ::OpenFileMapping(FILE_MAP_WRITE,FALSE,"ApplicationSpecificFileMappingName");
if (hmem)
    Msg = (TSharedMemory*)::MapViewOfFile(hmem,FILE_MAP_WRITE,0,0,sizeof(TSharedMemory));

When the client is finished, it should unmap a view to a file and close a handle obtained with ::OpenFileMapping().

::UnmapViewOfFile((LPVOID)Msg);
::CloseHandle(hmem);

Client must also create several named event objects and a named mutex object. Mutex HFree is created by each client. This mutex is used to synchronize access to the shared memory among the active clients. Using this mutex, only one client can access the shared memory at any time. Two events are already created by the server so that client will actually get a handle to already existing event. As described before, these two events are used for communication between the client who has access to the shared memory and the server.

HANDLE HFree = ::CreateMutex(NULL,FALSE,"ApplicationSpecificEventFree");
HANDLE HExec = ::CreateEvent(NULL,FALSE,FALSE,"ApplicationSpecificEventExec");
HANDLE HDone = ::CreateEvent(NULL,FALSE,FALSE,"ApplicationSpecificEventExec");

Client does not have to allocate a thread for communication with the server since this communication is always initiated by the client. However, if the communication to the server is performed from the main application thread, it can compromise the performance of the application since the thread is blocked until the complete communication session with the server is finished. The following pseudo-code describes how to communicate with the server.

retcode = 1;
if (::WaitForSingleObject(HFree,250) == WAIT_OBJECT_0) {
    // Fill a shared memory with data using Msg pointer
    .....
    // Signal Server to process this request
    ::SetEvent(HExec);
    if (::WaitForSingleObject(HDone,250) == WAIT_TIMEOUT) {
        retcode = 1;
    }
    // Server finished processing data
    // Handle data returned by the Server
    // Release mutex for others to send messages
    ::ReleaseMutex(HFree);

    retcode = 0;
}

return retcode;

In order to initiate communication with the server, client must first get ownership of the mutex (it will not get it immediatelly only if some other client is already communicating with the server). Once it gets the ownership of the mutex, client can safely access the shared memory. It populates the shared memory (for the purpose of this article, shared memory is represented with a structure TSharedMemory; client has a pointer variable Msg of type TSharedMemory) and signals the event HExec. Since this event if named, it is visible by the server. Server handles the data from shared memory, puts its own answer there and signals the event HDone. Client (who is waiting for the event HDone to be signalled) wakes up and handles the server answer. Only then will the server release ownership of the mutex HFree so that other clients can communicate with the server.

The only sensitive issue here are the timeout values for the two functions WaitForSingleObject. This should be determined by the programmer according to project requirements (it depends on how fast the server handles the data received by the client).

There are two other possibilities that must be considered in a real-world implementation. What happens if the client is started before the server or if the server is closed while the client is running.

If the client is started before the server, then ::OpenFileMapping() function will fail and return an invalid handle (since server has not created a file mapping object). This is detected by checking the value of a pointer to a shared memory object (in this case it is NULL). When a communication to the server is initiated by the client it should first check this pointer. If it is NULL, client should try to open a file mapping object and map a view to a file. Pointer value should be checked again. If it is still NULL, server is not running and client returns FALSE to the caller. If the server is started, everything works.

If the server is closed while the client is successfully connected to it (hmem and Msg have valid values), then ::WaitForSingleObject(HDone) will fail with a return code WAIT_TIMEOUT. The client assumes that the server is not running and should unmap a view to a file mapping object and close a handle to it in order to simulate the previous scenario (server is not started). Next call to the client is handled in a way described above.

Conclusion

This is a very powerful mechanism to transfer large ammount of data between the client and the server. It is also very fast (actual interprocess communication) and secure. However, it is limited to the local machine - it does not work via a network.

This article does not cover the issue of error handling which is very important in a real-world implementation.



Comments

  • about audio streaming

    Posted by Legacy on 05/24/2002 12:00am

    Originally posted by: BaoKhanh

    please send me source code audio streaming client/server
    thank!

    Reply
  • Use OpenEvent in client process

    Posted by Legacy on 03/25/2002 12:00am

    Originally posted by: Mouse

    i finally got this method to work, here's a tip.. If you are sure there is going to be only one instance of the server, you could use ::OpenEvent() in the client instead of ::CreateEvent(). If the server has not called ::CreateEvent(), ::OpenEvent() (in the client process) will return NULL letting you know that the server is not there or has not created the events yet. If you call ::CreateEvent() in the client, it will create the event dispite what is going on with the server.

    -mouse

    Reply
  • excellent

    Posted by Legacy on 03/24/2002 12:00am

    Originally posted by: Mouse

    exactly what i needed.
    excellent job on describing how this works.

    Reply
  • using mailslot in a service application

    Posted by Legacy on 12/11/2000 12:00am

    Originally posted by: yaron

    I read the program that you wrote and I want to transfer data by mailslot -its seems to be simpler.
    anyway when i`m using the mail slot in a server and tries to connect it from a pipe i get access denied... when it was in a normal application it worked fine.

    Reply
  • Shared memory problem

    Posted by Legacy on 10/05/2000 12:00am

    Originally posted by: Chen

    Hi:

    I use the same method for IPC on NT 4.0 SP 4, VC++6.0

    I have built two applications, one running as NT service. the other running as a normal Windows application for monitoring the service.

    I follow same procedure in both applications. First calling CreateFileMapping() followed by MapViewOfFile(). The problem is if I run NT service first, then run the Windows application, I get a failure saying ERROR_ACCESS_DENIED (05). But this problem doesn't occure if I swap the running order.

    The snapshot is provided.

    In NT service:

    bool CMyService::CreateSharedMem()
    {
    m_hMap =::CreateFileMapping((HANDLE)0xffffffff,
    0,
    PAGE_READWRITE,
    0,
    0x100000,
    "mysharedmem");

    if(m_hMap == NULL)
    return false;

    m_pSharedData =::MapViewOfFile(m_hMap,
    FILE_MAP_WRITE,
    0,
    0,
    0);

    if(m_pSharedData == NULL)
    return false;


    return true;

    }

    In monitoring Win application.

    bool CSpconsoleApp::CreateSharedMem()
    {
    m_hMap =::CreateFileMapping((HANDLE)0xffffffff, //or can be an open file handle
    0,
    PAGE_READWRITE,
    0,
    0x100000,
    "mysharedmem");

    if(m_hMap == NULL)
    return false;


    m_pSharedData =::MapViewOfFile(m_hMap,
    FILE_MAP_WRITE,
    0,
    0,
    0);

    if(m_pSharedData == NULL)
    {
    //Get ERROR_ACCESS_DENIED here
    int error = GetLastError();
    return false;
    }
    sharedMemAllocated = true;

    return true;

    }

    There is no sync. done in the 2 applications. But if that's the culprit, the problem shall happen regardless of the running order. And VC++ document says if file-mapping object already exist, then CreateFileMapping() is equal to OpenFileMapping().

    What could be the reason? Thank you in advance.

    Reply
  • Shared Memory(More than One Instance handling)

    Posted by Legacy on 11/01/1999 12:00am

    Originally posted by: M. Patil

    I have a program working using shared memory techniq. I need to run more than one instance of server and client. How can I change name of shared memory handle(If first instance is running) for second instance?. Any help is appreciated.

    Thanks

    Reply
  • Can Be Further Modified .....

    Posted by Legacy on 07/26/1999 12:00am

    Originally posted by: Rikhi Daman

    Hi!!
    
    

    I have gone through your documentation and I feel that it is no doubt an excellent work in this field.

    I wanted to modify this code saying that the client first searches it's local drive say 'c:/' to find the specific file he/she looking for. If the file is not found then he/she searches it in intranet assuming that the client is connected to intranet. If the file is found then it's served to the client else then the client request the remote server for the specif file (movie) of any format say avi or MPEG. If the file is found then it's served to the client else the client specify the URL address from where to download the movie. The server goes to that perticular cite and download the movie into its memory and then accept the request from client....

    I hope it sound good but need lot of hard work. So I request you if you can help me giving me with some pointers where I can find the sample code on the implementation of client server technology.

    Thanks...

    Rikhi Daman

    Reply
  • Shared memory class

    Posted by Legacy on 06/29/1999 12:00am

    Originally posted by: James

    Try this shared memory class posted on MSJ.

    http://www.microsoft.com/MSJ/1198/wicked/wicked1198top.htm

    Reply
  • Dear Sir/Madam,

    Posted by Legacy on 06/25/1999 12:00am

    Originally posted by: James

    Dear,

    Thank you for your information about IPC.
    I'd like to implement IPC using VC++(ver:6.0). Is it possible to open the source?. Please send it to my E-mail.

    Finally, I've worked for Hotel in Korea. I hope that I'll help another programmer about it.

    Thank you again.

    Sincerely,

    James,

    Reply
  • Client/Server interprocess communication via shared memory

    Posted by Legacy on 06/17/1999 12:00am

    Originally posted by: Arun Kumar S

    It's very convinient method ... sharing the memory in clint and server.

    The explanation is very impressive. But, In My case, as beginner...

    could You please provide a full sample program for the same.

    Please, Open your code or send your code to my e-mail.

    Keep up the good work

    Arun

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds