您的位置:首页 > 其它

Inter-Process Communication (IPC) Introduction and Sample Code

2012-03-02 18:03 387 查看

转自: http://www.codeproject.com/Articles/34073/Inter-Process-Communication-IPC-Introduction-and-S

Introduction

Inter-Process Communication (IPC) is a set of techniques for the exchange of data among multiple threads in one or more processes. Processes may be running on one or more computers connected by a network. IPC techniques include Named
Pipes, File Mapping, Mailslot, Remote Procedure Calls (RPC), etc.
In All-In-One Code Framework, we have already implemented samples (C++ and C#) for Named Pipes, File Mapping, Mail Slot, and Remoting. We are going to add more techniques like: Clickbord, Winsock, etc. You can download the latest code
from http://cfx.codeplex.com/.

Background

All-In-One Code Framework (short as AIO) delineates the framework and skeleton of most Microsoft development techniques (e.g., COM, Data Access, IPC)
using typical sample codes in different programming languages (e.g., Visual C#, VB.NET, Visual C++).

Using the Code

Find samples by following the steps below:

Download the zip file and unzip it.
Open the folder [Visual Studio 2008].
Open the solution file IPC.sln. You must pre-install Visual Studio 2008 on the machine.
In the Solution Explorer, open the [Process] \ [IPC and RPC] folder.

Samples Structure and Relationship



Named Pipe

Named pipes is a mechanism for one-way or bi-directional inter-process communication between a pipe server and one or more pipe clients in the local machine or across computers in an intranet:


Collapse | Copy
Code
PIPE_ACCESS_INBOUND:

Client (GENERIC_WRITE) ---> Server (GENERIC_READ)

PIPE_ACCESS_OUTBOUND:

Client (GENERIC_READ) <--- Server (GENERIC_WRITE)

PIPE_ACCESS_DUPLEX:

Client (GENERIC_READ or GENERIC_WRITE, or both)
<--> Server (GENERIC_READ and GENERIC_WRITE)

This sample demonstrates a named pipe server, \\.\pipe\HelloWorld, that supports
PIPE_ACCESS_DUPLEX
. It first creates such a named
pipe, then it listens to the client's connection. When a client is connected, the server attempts to read the client's requests from the pipe and writes a response.
A named pipe client attempts to connect to the pipe server, \\.\pipe\HelloWorld, with the
GENERIC_READ
and
GENERIC_WRITE
permissions.
The client writes a message to the pipe server and receives its response.

Code Logic

Server-side logic:

Create a named pipe. (
CreateNamedPipe
)
Wait for the client to connect. (
ConnectNamedPipe
)
Read client requests from the pipe and write the response. (
ReadFile
,
WriteFile
)
Disconnect the pipe, and close the handle. (
DisconnectNamedPipe
,
CloseHandle
)

Client-side logic:

Try to open a named pipe. (
CreateFile
)
Set the read mode and the blocking mode of the specified named pipe. (
SetNamedPipeHandleState
)
Send a message to the pipe server and receive its response. (
WriteFile
,
ReadFile
)
Close the pipe. (
CloseHandle
)

Code - CreateNamedPipe (C++)


Collapse | Copy
Code
// Create the named pipe.
HANDLE hPipe = CreateNamedPipe(

strPipeName,                      // The unique pipe name. This string must
// have the form of \\.\pipe\pipename
PIPE_ACCESS_DUPLEX,               // The pipe is bi-directional; both
// server and client processes can read
// from and write to the pipe
PIPE_TYPE_MESSAGE |               // Message type pipe
PIPE_READMODE_MESSAGE |           // Message-read mode
PIPE_WAIT,                        // Blocking mode is enabled
PIPE_UNLIMITED_INSTANCES,         // Max. instances

// These two buffer sizes have nothing to do with the buffers that
// are used to read from or write to the messages. The input and
// output buffer sizes are advisory. The actual buffer size reserved
// for each end of the named pipe is either the system default, the
// system minimum or maximum, or the specified size rounded up to the
// next allocation boundary. The buffer size specified should be
// small enough that your process will not run out of nonpaged pool,
// but large enough to accommodate typical requests.

BUFFER_SIZE,                      // Output buffer size in bytes
BUFFER_SIZE,                      // Input buffer size in bytes
NMPWAIT_USE_DEFAULT_WAIT,         // Time-out interval
&sa                               // Security attributes
)

For more code samples, please download AIO source code.

Security Attribute for Named Pipes

If
lpSecurityAttributes
of
CreateNamedPipe
is
NULL
,
the named pipe gets a default security descriptor and the handle cannot be inherited. The ACLs in the default security descriptor for a named pipe grants full control to the LocalSystem account, administrators, and the creator owner. They also grant read access
to members of the Everyone group and the anonymous account. In other words, with
NULL
as the security attribute, the named pipe cannot be connected with WRITE permission
across the network, or from a local client running as a lower integrity level. Here, we fill the security attributes to grant EVERYONE all access (not just the connect access) to the server. This solves the cross-network and cross-IL issues, but it creates
a security hole right there: the clients have WRITE_OWNER access and then the server just loses the control of the pipe object.

Code - Security Attributes (C++)


Collapse | Copy
Code
SECURITY_ATTRIBUTES sa;
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
// ACL is set as NULL in order to allow all access to the object.
SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, NULL, FALSE);
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;

.NET Named Pipe

.NET supports named pipes in two ways:

P/Invoke the native APIs.
By P/Invoke-ing the native APIs from .NET, we can mimic the code logic in
CppNamedPipeServer
to create the named pipe server, \\.\pipe\HelloWorld,
that supports
PIPE_ACCESS_DUPLEX
.
PInvokeNativePipeServer
first creates such a named pipe, then it listens to the client's connection. When a client is connected, the server
attempts to read the client's requests from the pipe and write a response.

System.IO.Pipes
namespace
In .NET Framework 3.5, the namespace
System.IO.Pipes
and a set of classes (e.g.,
PipeStream
,
NamedPipeServerStream
)
are added to the .NET BCL. These classes make the programming of named pipes in .NET much easier and safer than P/Invoke-ing the native APIs directly.
BCLSystemIOPipeServer
first creates such a named pipe, then it listens to the client's connection. When a client is connected, the server
attempts to read the client's requests from the pipe and write a response.

Code - Create Named Pipe (C#)


Collapse | Copy
Code
// Prepare the security attributes
// Granting everyone the full control of the pipe is just for
// demo purpose, though it creates a security hole.
PipeSecurity pipeSa = new PipeSecurity();
pipeSa.SetAccessRule(new PipeAccessRule("Everyone",
PipeAccessRights.ReadWrite, AccessControlType.Allow));

// Create the named pipe
pipeServer = new NamedPipeServerStream(
strPipeName,                    // The unique pipe name.
PipeDirection.InOut,            // The pipe is bi-directional
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Message,   // Message type pipe
PipeOptions.None,               // No additional parameters
BUFFER_SIZE,                    // Input buffer size
BUFFER_SIZE,                    // Output buffer size
pipeSa,                         // Pipe security attributes
HandleInheritability.None       // Not inheritable
);

File Mapping

File mapping is a mechanism for one-way or bi-directional inter-process communication among two or more processes in the local machine. To share a file or memory, all of the processes must use the name or the handle of the same file
mapping object.
To share a file, the first process creates or opens a file by using the
CreateFile
function. Next, it creates a file mapping object by
using the
CreateFileMapping
function, specifying the file handle and a name for the file mapping object. The names of events, semaphores, mutexes, waitable timers,
jobs, and file mapping objects share the same namespace. Therefore, the
CreateFileMapping
and
OpenFileMapping
functions
fail if they specify a name that is in use by an object of another type.
To share memory that is not associated with a file, a process must use the
CreateFileMapping
function and specify
INVALID_HANDLE_VALUE
as
the
hFile
parameter instead of an existing file handle. The corresponding file mapping object accesses memory backed by the system paging file. You must specify
a size greater than zero when you specify an
hFile
of
INVALID_HANDLE_VALUE
in
a call to
CreateFileMapping
.
Processes that share files or memory must create file views by using the
MapViewOfFile
or
MapViewOfFileEx
functions.
They must coordinate their access using semaphores, mutexes, events, or some other mutual exclusion techniques.
This example demonstrates a named shared memory server, Local\HelloWorld, that creates the file mapping object with
INVALID_HANDLE_VALUE
.
By using the
PAGE_READWRITE
flag, the process has read/write permission to the memory through any file view that is created.
The named shared memory client, Local\HelloWorld, can access the string written to the shared memory by the first process. The console displays the message "Message from the first process" that is read from the file mapping
created by the first process.

Code Logic

Service-side logic:

Create a file mapping. (
CreateFileMapping
)
Map the view of the file mapping into the address space of the current process. (
MapViewOfFile
)
Write message to the file view. (
CopyMemory
)
Unmap the file view and close the file mapping objects. (
UnmapViewOfFile
,
CloseHandle
)

Client-side logic:

Try to open a named file mapping. (
OpenFileMapping
)
Maps the view of the file mapping into the address space of the current process. (
MapViewOfFile
)
Read message from the view of the shared memory.
Unmap the file view and close the file mapping objects. (
UnmapViewOfFile
,
CloseHandle
)

Code - CreateFileMapping (C++)


Collapse | Copy
Code
// In terminal services: The name can have a "Global\" or "Local\" prefix
// to explicitly create the object in the global or session namespace.
// The remainder of the name can contain any character except the
// backslash character (\). For details, please refer to:
// http://msdn.microsoft.com/en-us/library/aa366537.aspx TCHAR szMapFileName[] = _T("Local\\HelloWorld");

// Create the file mapping object
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,      // Use paging file instead of existing file.
// Pass file handle to share in a file.

NULL,                      // Default security
PAGE_READWRITE,            // Read/write access
0,                         // Max. object size
BUFFER_SIZE,               // Buffer size
szMapFileName              // Name of mapping object
);

.NET only supports P/Invoke native APIs currently. By P/Invoke, .NET can simulate similar behaviors as native code.

Sample Code 4 (C# - P/Invoke)


Collapse | Copy
Code
/// <summary>
/// Creates or opens a named or unnamed file mapping object for
/// a specified file.
/// </summary>
/// <param name="hFile">A handle to the file from which to create
/// a file mapping object.</param>
/// <param name="lpAttributes">A pointer to a SECURITY_ATTRIBUTES
/// structure that determines whether a returned handle can be
/// inherited by child processes.</param>
/// <param name="flProtect">Specifies the page protection of the
/// file mapping object. All mapped views of the object must be
/// compatible with this protection.</param>
/// <param name="dwMaximumSizeHigh">The high-order DWORD of the
/// maximum size of the file mapping object.</param>
/// <param name="dwMaximumSizeLow">The low-order DWORD of the
/// maximum size of the file mapping object.</param>
/// <param name="lpName">The name of the file mapping object.
/// </param>
/// <returns>If the function succeeds, the return value is a
/// handle to the newly created file mapping object.</returns>
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFileMapping(
IntPtr hFile,                   // Handle to the file
IntPtr lpAttributes,            // Security Attributes
FileProtection flProtect,       // File protection
uint dwMaximumSizeHigh,         // High-order DWORD of size
uint dwMaximumSizeLow,          // Low-order DWORD of size
string lpName                   // File mapping object name
);

Mailslot

Mailslot is a mechanism for one-way inter-process communication in the local machine or across computers in the intranet. Any client can store messages in a mailslot. The creator of the slot, i.e., the server, retrieves the messages
that are stored there:


Collapse | Copy
Code
Client (GENERIC_WRITE) ---> Server (GENERIC_READ)

This sample demonstrates a mailslot server, \\.\mailslot\HelloWorld. It first creates such a mailslot, then it reads the new messages in the slot every five seconds. Then, a mailslot client connects and writes to the mailslot\\.\mailslot\HelloWorld.

Code Logic

Server-side logic:

Create a mailslot. (
CreateMailslot
)
Check messages in the mailslot. (
ReadMailslot
)

Check for the number of messages in the mailslot. (
GetMailslotInfo
)
Retrieve the messages one by one from the mailslot. While reading, update the number of messages that are left in the mailslot. (
ReadFile
,
GetMailslotInfo
)

Close the handle of the mailslot instance. (
CloseHandle
)

Client-side logic:

Open the mailslot. (
CreateFile
)
Write messages to the mailslot. (
WriteMailslot
,
WriteFile
)
Close the slot. (
CloseHandle
)

Code - GetMailslotInfo (C++)


Collapse | Copy
Code
/////////////////////////////////////////////////////////////////////////
// Check for the number of messages in the mailslot.
//
bResult = GetMailslotInfo(
hMailslot,                    // Handle of the mailslot
NULL,                         // No maximum message size
&cbMessageBytes,              // Size of next message
&cMessages,                   // Number of messages
NULL);                        // No read time-out

Code - CreateMailslot (C# - P/Invoke)


Collapse | Copy
Code
/// <summary>
/// Creates an instance of a mailslot and returns a handle for subsequent
/// operations.
/// </summary>
/// <param name="lpName">mailslot name</param>
/// <param name="nMaxMessageSize">The maximum size of a single message
/// </param>
/// <param name="lReadTimeout">The time a read operation can wait for a
/// message</param>
/// <param name="lpSecurityAttributes">Security attributes</param>
/// <returns>If the function succeeds, the return value is a handle to
/// the server end of a mailslot instance.</returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateMailslot(
string lpName,              // Mailslot name
uint nMaxMessageSize,       // Max size of a single message in bytes
int lReadTimeout,           // Timeout of a read operation
IntPtr lpSecurityAttributes // Security attributes
);

Remoting

.NET Remoting is a mechanism for one-way inter-process communication and RPC between .NET applications in the local machine or across computers in the intranet and internet.
.NET Remoting allows an application to make a remotable object available across remoting boundaries, which includes different appdomains, processes, or even different computers connected by a network. .NET Remoting makes a reference
of a remotable object available to a client application, which then instantiates and uses a remotable object as if it were a local object. However, the actual code execution happens at the server-side. All requests to the remotable object are proxied by the
.NET Remoting runtime over Channel objects that encapsulate the actual transport mode, including TCP streams, HTTP streams, and named pipes. As a result, by instantiating proper Channel objects, a .NET Remoting application can be made to support different
communication protocols without recompiling the application. The runtime itself manages the act of serialization and marshalling of objects across the client and server appdomains.

Code - Create and Register a Channel (C#)


Collapse | Copy
Code
/////////////////////////////////////////////////////////////////////
// Create and register a channel (TCP channel in this example) that
// is used to transport messages across the remoting boundary.
//
// Properties of the channel
IDictionary props = new Hashtable();
props["port"] = 6100;   // Port of the TCP channel
props["typeFilterLevel"] = TypeFilterLevel.Full;
// Formatters of the messages for delivery
BinaryClientFormatterSinkProvider clientProvider = null;
BinaryServerFormatterSinkProvider serverProvider =
new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

// Create a TCP channel
TcpChannel tcpChannel = new TcpChannel(props, clientProvider, serverProvider);

// Register the TCP channel
ChannelServices.RegisterChannel(tcpChannel, true);

Code - Register Remotable Types (VB.NET)


Collapse | Copy
Code
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Register the remotable types on the service end as
' server-activated types (aka well-known types) or client-activated
' types.
' Register RemotingShared.SingleCallObject as a SingleCall server-
' activated type.
RemotingConfiguration.RegisterWellKnownServiceType(GetType(RemotingShared.SingleCallObject), _
"SingleCallService", WellKnownObjectMode.SingleCall)
' Register RemotingShared.SingletonObject as a Singleton server-
' activated type.
RemotingConfiguration.RegisterWellKnownServiceType(GetType(RemotingShared.SingletonObject), _
"SingletonService", WellKnownObjectMode.Singleton)
' Register RemotingShared.ClientActivatedObject as a client-
' activated type.
RemotingConfiguration.ApplicationName = "RemotingService"
RemotingConfiguration.RegisterActivatedServiceType(_
GetType(Global.RemotingShared.ClientActivatedObject))

Points of Interest

In the pilot phase of the AIO project, we focus on five techniques: COM, Library, IPC, Office, and Data Access. There has been 42 code examples in the project. The collection currently grows at a rate of seven examples per week.

History

This article was created on 3/12/2009.


License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: