Simple Point to Point Communication Using Named Pipes

(NTier Solutions AB).

Environment: VC6 SP3, NT4 SP5

Introduction

In a project I needed inter process communication and the use of named pipes seamed to be the most appropriate solution as well as the recommended when I discussed the problem (and requirements) with an other experienced developer.
Hence I started to search MSDN and CodeGuru for some nice reusable code using named pipes. At CodeGuru I found nothing usable and the code found in MSDN was ugly and not easy to reuse. Anxious to get progress, I started experimenting my self to get something that worked for me.

What I needed was for one server to communicate with several other processes and to keep it simple the server and one client only should share the same pipe. Both server and client should be able to communicate with the other at any given time. The result is the class CNamedPipe that actually uses two "one-way"-pipes and a listener thread on each side (i.e. Server and Client side). And I had the advantage of knowing that the server always has created the pipes correctly before the client tries to open them (saves me from some error handling). See the source file for further details on implementation.

It is important to remember that my implementation is simple and do not use (or support) all the fancy options available when using named pipes. Note also that the project for which the final version of CNamedPipe was intended for was non-MFC and hence the STL templates are used for strings (means some STL related warnings when compiling with warning level 4).

Basic piping

The named pipe must have a name. It looks like this:
\\ServerName\PIPE\pipe
The bold parts (including back slashes) are mandatory. ServerName should be a dot (i.e. \\.\PIPE\...) if the pipe is created (the creator will be called the server side of the pipe or the computer name if the name is to be used by a client.
pipe is a name used to uniquely identify a pipe on a certain computer. Note that pipe names are always case insensitive.

The server side of a pipe is the creator of a pipe and the pipe may be created with read, write or read/write access. For full information on how to use different options, check MSDN help file for CreateNamedPipe.

CNamedPipe creates a pipe like this:

m_hInPipe = CreateNamedPipe(
 GetRealPipeName(true).c_str(), //Gets the Pipename
 PIPE_ACCESS_INBOUND,           //Server will read from this pipe
                                //Client will write
 PIPE_WAIT,                     //Do not return Read/write calls
                                //until read/write is complete
 1,                             //allow only 1 instance of this pipe
 PIPE_BUF_SIZE,                 //Macro defined by me.
 PIPE_BUF_SIZE,                 //Macro defined by me.
 PIPE_TIMEOUT,                  //Macro defined by me.
 NULL);                         //Do not inherit security.

A client then connects to the pipe like this:

m_hOutPipe = 
 CreateFile(GetRealPipeName(true).c_str(), //Gets the Pipename
 GENERIC_WRITE,           //Client only writes to this pipe.
 0,                       //Do not share this pipe with others.
 NULL,                    //Do not inherit security.
 OPEN_EXISTING,           //Pipe must exist.
 FILE_ATTRIBUTE_NORMAL,   //I have no special requirements on 
                          //file attributes
 NULL);                   //This parameter not used since pipe 
                          //created with PIPE_WAIT.

When using pipes it is important that the client opens the pipe with correct options. If the server will only read from the pipe, the client must open it as write only etc.

When reading and writing from pipes, one just uses the functions ReadFile and WriteFile. If the PIPE_WAIT flag is used, the ReadFile function will not return until something is written to the pipe and vice versa. Hence it is important to create a separate thread which tries to read from the pipe. This thread will be waiting until a WriteFile operation is made by the other side.

Features in CNamedPipe

bool Initialize(bool bAsServer, 
                string szStopListnenCmd,
                void (WINAPI *fCallBack)(string buf))

Initialize must be called to create/open the pipes. The server must be initialized first. To Initialize the server, parameter bAsServer should be true (hence false when initializing the client). The szStopListnenCmd parameter is used to tell the listener thread to stop listen. When the listening thread receives a message identical to this parameter (case sensitive), the thread will exit its listening loop. The last parameter is a function pointer and this function is called for every completed successful ReadFile the listening thread performs. The buffer read will be the argument of the call.

SetPipeName and GetRealPipeName are used to set and get the used names. SetPipeName takes two arguments and makes a string like this:
\\<arg2>\PIPE\<arg1>
GetRealPipeName uses the constructed string and adds "_IN" or "_OUT" depending on its argument. So the used names are:
Server reads from: \\<arg2>\PIPE\<arg1>_IN
Server writes to: \\<arg2>\PIPE\<arg1>_OUT

PipeTest - a demo of CNamedPipe

Running demo as server
Running demo as client

Only one client per server is possible. Server name must be the server computer's entry in DNS.

To see examples of the use of CNamedPipe, check the following functions in class CPipeTestDlg:

  • OnBConnect - Creating pipes...
  • OnBSend - Send a message to the pipe...
  • funcCallBack - implementation of the callback function called by the CNamedPipe listener thread.

Downloads

Download demo project - 18 Kb
Download source - 3 Kb


Comments

  • Little fix...

    Posted by aurelien on 08/22/2006 10:28am

    hi, and sorry for my english
    
    when I tried this project, I have noticed that it consumed much CPU time. so this is my little piece of code for fix it :
    
    /*static*/
    DWORD WINAPI CNamedPipe::ListnerProc(LPVOID lpParameter)
    {
    	ListnerParam* pLP = (ListnerParam*)lpParameter;
    	if (pLP == NULL)
    		return 1;
    
    	DWORD dwRetVal = 0;
    	for (bool bContinue = true; bContinue && dwRetVal == 0; Sleep( 10 )/* <- little sleep */ )
    	{
    		char buf[PIPE_BUF_SIZE];
    		DWORD lpNumberOfBytesRead = 0, dwRead = 0;
    		BOOL bOK;
    		
    		// Test the total number of bytes available to be read from the pipe
    		PeekNamedPipe( pLP->hPipe, NULL, 0, NULL, &lpNumberOfBytesRead, NULL);
    		if (lpNumberOfBytesRead == 0)
    			continue;
    		
    		bOK = ReadFile( pLP->hPipe, buf, PIPE_BUF_SIZE, &dwRead, NULL );				
    
    		if (!bOK)
    			dwRetVal = 2;
    
    		string szMsg = buf;
    		
    		if (szMsg == pLP->szStopCmd)
    			bContinue = false;
    
    		pLP->funcCallBack( &buf, dwRead );
    
    		// Add-on for multiple pipe write :p
    		// DisconnectNamedPipe( pLP->hPipe );
    		// ConnectNamedPipe( pLP->hPipe, NULL );
    		
    	}
    	delete pLP;
    	return dwRetVal;
    }
    
    
    I hop it will help somebody

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

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • The hard facts on SaaS adoption in over 80,000 enterprises: Public vs. private companies Mid-market vs. large enterprise GoogleApps, Office365, Salesforce & more Why security is a growing concern Fill out the form to download the full cloud adoption report.

Most Popular Programming Stories

More for Developers

RSS Feeds