Client/Server interprocess communication via Shared Memory
- 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:00amOriginally posted by: BaoKhanh
please send me source code audio streaming client/server
Replythank!
Use OpenEvent in client process
Posted by Legacy on 03/25/2002 12:00amOriginally 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
Replyexcellent
Posted by Legacy on 03/24/2002 12:00amOriginally posted by: Mouse
exactly what i needed.
Replyexcellent job on describing how this works.
using mailslot in a service application
Posted by Legacy on 12/11/2000 12:00amOriginally posted by: yaron
I read the program that you wrote and I want to transfer data by mailslot -its seems to be simpler.
Replyanyway 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.
Shared memory problem
Posted by Legacy on 10/05/2000 12:00amOriginally 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.
ReplyShared Memory(More than One Instance handling)
Posted by Legacy on 11/01/1999 12:00amOriginally 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
ReplyCan Be Further Modified .....
Posted by Legacy on 07/26/1999 12:00amOriginally posted by: Rikhi Daman
ReplyShared memory class
Posted by Legacy on 06/29/1999 12:00amOriginally posted by: James
Try this shared memory class posted on MSJ.
http://www.microsoft.com/MSJ/1198/wicked/wicked1198top.htm
ReplyDear Sir/Madam,
Posted by Legacy on 06/25/1999 12:00amOriginally 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,
ReplyClient/Server interprocess communication via shared memory
Posted by Legacy on 06/17/1999 12:00amOriginally 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
ReplyLoading, Please Wait ...