Chapter 11. Interprocess Communication

Chapter 6 showed how to create and manage processes, and Chapters 7 to 10 showed how to manage and synchronize threads within processes. So far, however, we have not been able to perform direct process-to-process communication other than through shared memory (Chapter 5).

The next step is to provide sequential interprocess communication (IPC) between processes1 using filelike objects. Two primary Windows mechanisms for IPC are the anonymous pipe and the named pipe, both of which are accessed with the familiar ReadFile and WriteFile functions. Simple anonymous pipes are character-based and half-duplex. As such, they are well suited for redirecting the output of one program to the input of another, as is common with communicating Linux and UNIX programs. The first example shows how to do this with Windows anonymous pipes.

1 The Windows system services also allow processes to communicate through mapped files, as demonstrated in the semaphore exercise in Chapter 10 (Exercise 10–10). Additional mechanisms for IPC include files, sockets, remote procedure calls, COM, and message posting. Chapter 12 describes sockets.

Named pipes are much more powerful than anonymous pipes. They are full-duplex and message-oriented, and they allow networked communication. Furthermore, there can be multiple open handles on the same pipe. These capabilities, coupled with convenient transaction-oriented named pipe functions, make named pipes appropriate for creating client/server systems. This capability is shown in this chapter’s second example, a multithreaded client/server command processor, modeled after Figure 7-1, which was used to introduce threads. Each server thread manages communication with a different client, and each thread/client pair uses a distinct handle, or named pipe instance. Mailslots, which allow for one-to-many message broadcasting and are also filelike, are used to help clients locate servers.

Anonymous Pipes

Windows anonymous pipes allow one-way (half-duplex), byte-based IPC. Each pipe has two handles: a read handle and a write handle. The CreatePipe function is:

The pipe handles are often inheritable; the next example shows the reasons. cbPipe, the pipe byte size, is only a suggestion, and 0 specifies the default value.

In order to use the pipe for IPC, there must be another process, and that process requires one of the pipe handles. Assume that the parent process, which calls CreatePipe, wishes to write data for a child to use. The problem, then, is to communicate the read handle (phRead) to the child. The parent achieves this by setting the child procedure’s input handle in the start-up structure to *phRead (see Chapter 6 for process management and the start-up structure).

Reading a pipe read handle will block if the pipe is empty. Otherwise, the read will accept as many bytes as are in the pipe, up to the number specified in the ReadFile call. A write operation to a full pipe, which is implemented in a memory buffer, will also block.

Finally, anonymous pipes are one-way. Two pipes are required for bidirectional communication.

Example: I/O Redirection Using an Anonymous Pipe

Program 11-1 shows a parent process, Redirect, that creates two processes from the command line and pipes them together. The parent process sets up the pipe and redirects standard input and output. Notice how the anonymous pipe handles are inheritable and how standard I/O is redirected to the two child processes; these techniques were described in Chapter 6.

Program 11-1 Redirect: Interprocess Communication

image

image

image

The location of WriteFile in Program2 on the right side of Figure 11-1 assumes that the program reads a large amount of data, processes it, and then writes out results. Alternatively, the write could be inside the loop, putting out results after each read.

Figure 11-1 Process-to-Process Communication Using an Anonymous Pipe

image

Close the pipe and thread handles at the earliest possible point. Figure 11-1 does not show the handle closings, but Program 11-1 does. The parent should close the standard output handle immediately after creating the first child process so that the second process will be able to recognize an end of file when the first process terminates. If there were still an open handle, the second process might not terminate because the application would not indicate an end of file.

Program 11-1 uses an unusual syntax; the = sign is the pipe symbol separating the two commands. The vertical bar ( | ) would conflict with the command processor. Figure 11-1 schematically shows the execution of the command:

$ Redirect Program1 arguments = Program2 arguments

In UNIX or at the Windows command prompt, the corresponding command would be:

$ Program1 arguments | Program2 arguments

Run 11-1 shows output from grepMT, Chapter 7’s multithreaded pattern search program piped to FIND, which is a similar Windows command. While this may seem a bit artificial, cat is the book’s only sample program that accepts standard input, and it also shows that Redirect works with third-party programs that accept standard input.

Run 11-1 Redirect: Using an Anonymous Pipe

image

These examples search the presidents and monarchs files, first used in Chapter 6, for individuals named “James” and “George” who lived in any part of the eighteenth century (the search is not entirely accurate) or “William” who lived in any part of the nineteenth century. The file names were shortened to decrease the horizontal space.

Named Pipes

Named pipes have several features that make them an appropriate general-purpose mechanism for implementing IPC-based applications, including networked file access and client/server systems,2 although anonymous pipes remain a good choice for simple byte-stream IPC, such as the preceding example, where communication is within a single computer. Named pipe features (some are optional) include the following.

2 This statement requires a major qualification. Windows Sockets (Chapter 12) is the preferred API for most networking applications and higher-level protocols (http, ftp, and so on), especially where TCP/IP-based interoperability with non-Windows systems is required. Many developers prefer to limit named pipe usage to IPC within a single computer or to communication within Windows networks.

• Named pipes are message-oriented, so the reading process can read varying-length messages precisely as sent by the writing process.

• Named pipes are bidirectional, so two processes can exchange messages over the same pipe.

• There can be multiple, independent instances of pipes with the same name. For example, several clients can communicate concurrently with a single server using distinct instances of a named pipe. Each client can have its own named pipe instance, and the server can respond to a client using the same instance.

• Networked clients can access the pipe by name. Named pipe communication is the same whether the two processes are on the same machine or on different machines.

• Several convenience and connection functions simplify named pipe request/response interaction and client/server connection.

Named pipes are generally preferable to anonymous pipes, although Program 11-1 and Figure 11-1 did illustrate a situation in which anonymous pipes are useful. Use named pipes any time your communication channel needs to be bidirectional, message-oriented, networked, or available to multiple client processes. The upcoming examples could not be implemented using anonymous pipes.

Using Named Pipes

CreateNamedPipe creates the first instance of a named pipe and returns a handle. The function also specifies the pipe’s maximum number of instances and, hence, the number of clients that can be supported simultaneously.

Normally, the creating process is regarded as the server. Client processes, possibly on other systems, open the pipe with CreateFile.

Figure 11-2 shows an illustrative client/server relationship, and the pseudocode shows one scheme for using named pipes. Notice that the server creates multiple instances of the same pipe, each of which can support a client. The server also creates a thread for each named pipe instance, so that each client has a dedicated thread and named pipe instance. Figure 11-2, then, shows how to implement the multithreaded server model of Figure 7-1.

Figure 11-2 Clients and Servers Using Named Pipes

image

Creating Named Pipes

Here is the specification of the CreateNamedPipe function.

Parameters

lpName indicates the pipe name, which must be of the form:

\.pipepipename

The period (.) stands for the local machine; thus, you cannot create a pipe on a remote machine. The pipename is case-insensitive, can be up to 256 characters long, and can contain any character other than backslash.

dwOpenMode specifies several flags; the important ones for our purposes are:

• One of three mutually exclusive data flow description flags—PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, or PIPE_ACCESS_OUTBOUND. The value determines the combination of GENERIC_READ and GENERIC_WRITE from the server’s perspective. Thus, PIPE_ACCESS_INBOUND gives the server GENERIC_READ access, and the client must use GENERIC_WRITE when connecting with CreateFile. If the access is PIPE_ACCESS_DUPLEX, data flows bidirectionally, and the client can specify GENERIC_READ, GENERIC_WRITE, or both.

FILE_FLAG_OVERLAPPED enables asynchronous I/O (Chapter 14).

The mode can also specify FILE_FLAG_WRITE_THROUGH (not used with message pipes), FILE_FLAG_FIRST_PIPE_INSTANCE, and more (see MSDN).

dwPipeMode has three mutually exclusive flag pairs. They indicate whether writing is message-oriented or byte-oriented, whether reading is by messages or blocks, and whether read operations block.

PIPE_TYPE_BYTE and PIPE_TYPE_MESSAGE indicate whether data is written to the pipe as a stream of bytes or messages. Use the same type value for all pipe instances with the same name.

PIPE_READMODE_BYTE and PIPE_READMODE_MESSAGE indicate whether data is read as a stream of bytes or messages. PIPE_READMODE_MESSAGE requires PIPE_TYPE_MESSAGE.

PIPE_WAIT and PIPE_NOWAIT determine whether ReadFile will block. Use PIPE_WAIT because there are better ways to achieve asynchronous I/O.

nMaxInstances determines the maximum number of pipe instances. As Figure 11-2 shows, use this same value for every CreateNamedPipe call for a given pipe. Use the value PIPE_UNLIMITED_INSTANCES to have Windows base the number on available computer resources.

nOutBufferSize and nInBufferSize give the sizes, in bytes, of the input and output buffers used for the named pipes. Specify 0 to get default values.

nDefaultTimeOut is a default time-out period (in milliseconds) for the WaitNamedPipe function, which is discussed in an upcoming section. This situation, in which the create function specifies a time-out for a related function, is unique.

The error return value is INVALID_HANDLE_VALUE because pipe handles are similar to file handles.

lpSecurityAttributes operates as in all the other create functions.

The first CreateNamedPipe call actually creates the named pipe and an instance. Closing the last handle to an instance will delete the instance (usually, there is only one handle per instance). Closing the last instance of a named pipe will delete the pipe, making the pipe name available for reuse.

Named Pipe Client Connections

Figure 11-2 shows that a client connects to a named pipe using CreateFile with the pipe name. In many cases, the client and server are on the same machine, and the name would take this form:

\.pipe[path]pipename

If the server is on a different machine, the name would take this form:

\servernamepipepipename

Using the name period (.) when the server is local—rather than using the local machine name—delivers significantly better connection-time performance.

Named Pipe Status Functions

There are seven functions to interrogate pipe status information, and an eighth sets state information. They are mentioned briefly, and Program 11-3 demonstrates several of the functions.

GetNamedPipeHandleState returns information, given an open handle, on whether the pipe is in blocking or nonblocking mode, whether it is message-oriented or byte-oriented, the number of pipe instances, and so on.

SetNamedPipeHandleState allows the program to set the same state attributes. The mode and other values are passed by address rather than by value, which is necessary so that a NULL value specifies that the mode should not be changed. See the full Examples code of Program 11-2 for an example.

Program 11-2 clientNP: Named Pipe Connection-Oriented Client

image

image

GetNamedPipeInfo determines whether the handle is for a client or server instance, the buffer sizes, and so on.

• Five functions get information about the client name and the client and server session ID and process ID. Representative names are GetNamedPipeClientSessionId and GetNamedPipeServerProcessId.

Named Pipe Connection Functions

The server, after creating a named pipe instance, can wait for a client connection (CreateFile or CallNamedPipe, described in a subsequent function) using ConnectNamedPipe.

With lpOverlapped set to NULL, ConnectNamedPipe will return as soon as there is a client connection. Normally, the return value is TRUE. However, it would be FALSE if the client connected between the server’s CreateNamedPipe call and the ConnectNamedPipe call. In this case, GetLastError returns ERROR_PIPE_CONNECTED, and the connection is valid despite the FALSE return value.

Following the return from ConnectNamedPipe, the server can read requests using ReadFile and write responses using WriteFile. Finally, the server should call DisconnectNamedPipe to free the handle (pipe instance) for connection with another client.

WaitNamedPipe, the final function, is for use by the client to synchronize connections to the server. The call will return successfully as soon as the server has a pending ConnectNamedPipe call. By using WaitNamedPipe, the client can be certain that the server is ready for a connection and the client can then call CreateFile. Nonetheless, the client’s CreateFile call could fail if some other client opens the named pipe using CreateFile or if the server closes the instance handle; that is, there is a race involving the server and the clients. The server’s ConnectNamedPipe call will not fail. Notice that there is a time-out period for WaitNamedPipe that, if specified, will override the time-out period specified with the server’s CreateNamedPipe call.

Client and Server Named Pipe Connection

The proper connection sequences for the client and server are as follows. First is the server sequence, in which the server makes a client connection, communicates with the client until the client disconnects (causing ReadFile to return FALSE), disconnects the server-side connection, and then connects to another client.

image

The client connection sequence is as follows, where the client terminates after it finishes, allowing another client to connect on the same named pipe instance. As shown, the client can connect to a networked server if it knows the server name.

image

Notice the race conditions between the client and the server. First, the client’s WaitNamedPipe call will fail if the server has not yet created the named pipe; the failure test is omitted for brevity but is included in the sample programs in the Examples file. Next, the client may, in rare circumstances, complete its CreateFile call before the server calls ConnectNamedPipe. In that case, ConnectNamedPipe will return FALSE to the server, but the named pipe communication will still function properly.

The named pipe instance is a global resource, so once the client disconnects, another client can connect with the server.

Named Pipe Transaction Functions

Figure 11-2 shows a typical client configuration in which the client does the following:

• Opens an instance of the pipe, creating a long-lived connection to the server and consuming a pipe instance

• Repetitively sends requests and waits for responses

• Closes the connection

The common WriteFile, ReadFile sequence could be regarded as a single client transaction, and Windows provides such a function for message pipes.

The parameter usage is clear because this function combines WriteFile and ReadFile on the named pipe handle. Both the output and input buffers are specified, and *lpcbRead receives the message length. Overlapped operations (Chapter 14) are possible. More typically, the function waits for the response.

TransactNamedPipe is convenient, but, as in Figure 11-2, it requires a permanent connection, which limits the number of clients.3

3 Note that TransactNamedPipe is more than a mere convenience compared with WriteFile and ReadFile and can provide some performance advantages. One experiment shows throughput enhancements ranging from 57% (small messages) to 24% (large messages).

CallNamedPipe is the second client convenience function:

CallNamedPipe does not require a permanent connection; instead, it makes a temporary connection by combining the following complete sequence:

image

into a single function. The benefit is that clients do not have long-lived connections, and the server can service more clients at the cost of per-request connection overhead.

The parameter usage is similar to that of TransactNamedPipe except that a pipe name, rather than a handle, specifies the pipe. CallNamedPipe is synchronous (there is no overlapped structure). It specifies a time-out period, in milliseconds, for the connection but not for the transaction. There are three special values for dwTimeOut:

NMPWAIT_NOWAIT

NMPWAIT_WAIT_FOREVER

NMPWAIT_USE_DEFAULT_WAIT, which uses the default time-out period specified by CreateNamedPipe

Peeking at Named Pipe Messages

In addition to reading a named pipe using ReadFile, you can also determine whether there is actually a message to read using PeekNamedPipe. This is useful to poll the named pipe (an inefficient operation), determine the message length so as to allocate a buffer before reading, or look at the incoming data so as to prioritize its processing.

PeekNamedPipe nondestructively reads any bytes or messages in the pipe, but it does not block; it returns immediately.

Test *lpcbAvail to determine whether there is data in the pipe; if there is, *lpcbAvail will be greater than 0. lpBuffer and lpcbRead can be NULL, but if you need to look at the data, call PeekNamedPipe a second time with a buffer and count large enough to receive the data (based on the *lpcbAvail value). If a buffer is specified with lpBuffer and cbBuffer, then *lpcbMessage will tell whether there are leftover message bytes that could not fit into the buffer, allowing you to allocate a large buffer before reading from the named pipe. This value is 0 for a byte mode pipe.

Again, PeekNamedPipe reads nondestructively, so a subsequent ReadFile is required to remove messages or bytes from the pipe.

Example: A Client/Server Command Line Processor

Everything required to build a request/response client/server system is now available. This example is a command line server that executes a command on behalf of the client. Features of the system include:

• Multiple clients can interact with the server.

• The clients can be on different systems on the network, although the clients can also be on the server machine.

• The server is multithreaded, with a thread dedicated to each named pipe instance. That is, there is a thread pool of worker threads4 ready for use by connecting clients. Worker threads are allocated to a client on the basis of the named pipe instance that the system allocates to the client.

4 This application-managed thread pool is different from the NT6 thread pool (see Chapter 10).

• The individual server threads process a single request at a time, simplifying concurrency control. Each thread handles its own requests independently. Nonetheless, exercise the normal precautions if different server threads are accessing the same file or other resource.

Program 11-2 shows the single-threaded client, and its server is Program 11-3. The server corresponds to the model in Figures 7-1 and 11-2. The client request is simply the command line. The server response is the resulting output, which is sent in several messages. The programs also use the include file ClientServer.h, which is included in the Examples file, and defines the request and response data structures as well as the client and server pipe names.

Program 11-3 serverNP: Multithreaded Named Pipe Server Program

image

image

image

image

image

image

image

The client in Program 11-2 also calls a function, LocateServer, which finds a server pipe by name. LocateServer uses a mailslot, described in a later section and shown in Program 11-5.

The defined records have DWORD32 length fields; this is done to emphasize the field size.

Program 11-3 is the server program, including the server thread function, that processes the requests from Program 11-2. The server also creates a “server broadcast” thread (see Program 11-4) to broadcast its pipe name on a mailslot to clients that want to connect. Program 11-2 calls the LocateServer function, shown in Program 11-5, which reads the information sent by this process. Mailslots are described later in this chapter.

Program 11-4 SrvrBcst: Mailslot Client Thread Function

image

Program 11-5 LocSrver: Mailslot Server

image

While the code is omitted in Program 11-4, the server (in the Examples file) optionally secures its named pipe to prevent access by unauthorized clients. Chapter 15 describes object security and how to use this option. Also, see the example for the server process shutdown logic.

Comments on the Client/Server Command Line Processor

This solution includes a number of features as well as limitations that will be addressed in later chapters.

• Multiple client processes can connect with the server and perform concurrent requests; each client has a dedicated server (or worker) thread allocated from the thread pool.

• The server and clients can run from separate command prompts or can run under control of JobShell (Program 6-3).

• If all the named pipe instances are in use when a client attempts to connect, the new client will wait until a different client disconnects on receiving a $Quit command, making a pipe instance available for another client. Several new clients may be attempting to connect concurrently and will race to open the available instance; threads that lose this race will need to wait again.

• Each server thread performs synchronous I/O, but some server threads can be processing requests while others are waiting for connections or client requests.

• Extension to networked clients is straightforward, subject to the limitations of named pipes discussed earlier in this chapter. Simply change the pipe names in the header file or add a client command line parameter for the server name.

• Each server worker thread creates a simple connection thread, which calls ConnectNamedPipe and terminates as soon as a client connects. This allows a worker thread to wait, with a time-out, on the connection thread handle and test the global shutdown flag periodically. If the worker threads blocked on ConnectNamedPipe, they could not test the flag and the server could not shut down. For this reason, the server thread performs a CreateFile on the named pipe in order to force the connection thread to resume and shut down. Asynchronous I/O (Chapter 14) is an alternative, so that an event could be associated with the ConnectNamedPipe call. The comments in the Examples file source provide additional alternatives and information. Without this solution, connection threads might never terminate by themselves, resulting in resource leaks. Chapter 12 discusses this subject.

• There are a number of opportunities to enhance the system. For example, there could be an option to execute an in-process server by using a DLL that implements some of the commands. This enhancement is added in Chapter 12.

• The number of server threads is limited by the WaitForMultipleObjects call in the main thread. While this limitation is easily overcome, the system here is not truly scalable; too many threads will impair performance, as we saw in Chapter 10. Chapter 14 uses asynchronous I/O ports to address this issue.

Running the Client and Server

The details of how clients locate servers are explained in the next section (“Mailslots”). However, we can now show the programs in operation. Run 11-3 shows the server, Program 11-3, which was started using JobShell from Chapter 6. The server accepts connections from three client processes, reporting the connections and the commands.

Run 11-3 serverNP: Servicing Several Clients

image

Run 11-4 shows one of the clients in operation; this is the client represented by process ID 15872 in Run 11-3. The commands are familiar from previous chapters.

Run 11-4 clientNP: Client Commands and Results

image

Mailslots

A Windows mailslot, like a named pipe, has a name that unrelated processes can use for communication. Mailslots are a broadcast mechanism, similar to datagrams (see Chapter 12), and behave differently from named pipes, making them useful in some important but limited situations. Here are the significant mailslot characteristics:

• A mailslot is one-directional.

• A mailslot can have multiple writers and multiple readers, but frequently it will be one-to-many of one form or the other.

• A writer (client) does not know for certain that all, some, or any readers (servers) actually received the message.

• Mailslots can be located over a network domain.

• Message lengths are limited.

Using a mailslot requires the following operations.

• Each server creates a mailslot handle with CreateMailslot.

• The server then waits to receive a mailslot message with a ReadFile call.

• A write-only client should open the mailslot with CreateFile and write messages with WriteFile. The open will fail (name not found) if there are no waiting readers.

A client’s message can be read by all servers; all of them receive the same message.

There is one further possibility. The client, in performing the CreateFile, can specify a name of this form:

\*mailslotmailslotname

In this way, the * acts as a wildcard, and the client can locate every server in the domain, a networked group of systems assigned a common name by the network administrator. The client can then connect to one of the servers, assuming that they all provide the same basic functionality, although the server responses could contain information (current load, performance, etc.) that would influence the client’s choice.

Using Mailslots

The preceding client/server command processor suggests several ways that mailslots might be useful. Here is one scenario that will solve the server location problem in the preceding client/server system (Programs 11-2 and 11-3).

The application server, acting as a mailslot client, periodically broadcasts its name and a named pipe name. Any application client that wants to find a server can receive this name by being a mailslot server. In a similar manner, the command line server can periodically broadcast its status, including information such as utilization, to the clients. This situation could be described as a single writer (the mailslot client) and multiple readers (the mailslot servers). If there were multiple mailslot clients (that is, multiple application servers), there would be a many-to-many situation.

Alternatively, a single reader could receive messages from numerous writers, perhaps giving their status—that is, there would be multiple writers and a single reader. This usage, for example, in a bulletin board application, justifies the term mailslot. These first two uses—name and status broadcast—can be combined so that a client can select the most appropriate server.

The inversion of the terms client and server is confusing in this context, but notice that both named pipe and mailslot servers perform the CreateNamedPipe (or CreateMailSlot) calls, while the client (named pipe or mailslot) connects using CreateFile. Also, in both cases, the client performs the first WriteFile and the server performs the first ReadFile.

Figure 11-3 shows the use of mailslots for the first approach.

Figure 11-3 Clients Using a Mailslot to Locate a Server

image

Creating and Opening a Mailslot

The mailslot servers (readers) use CreateMailslot to create a mailslot and to get a handle for use with ReadFile. There can be only one mailslot of a given name on a specific machine, but several systems in a network can use the same name to take advantage of mailslots in a multireader situation.

Parameters

lpName points to a mailslot name of this form:

\.mailslot[path]name

The name must be unique. The period (.) indicates that the mailslot is created on the current machine. The path, if any, represents a pseudo directory, and path components are separated by backslash characters.

cbMaxMsg is the maximum size (in bytes) for messages that a client can write. A value of 0 means no limit.

dwReadTimeout is the number of milliseconds that a read operation will wait. A value of 0 causes an immediate return, and MAILSLOT_WAIT_FOREVER is an infinite wait (no time-out).

The client (writer), when opening a mailslot with CreateFile, can use the following name forms.

\.mailslot[path]name specifies a local mailslot.

\computernamemailslot[path]name specifies a mailslot on a specified machine.

\domainnamemailslot[path]name specifies all mailslots on machines in the domain. In this case, the maximum message length is 424 bytes.

\*mailslot[path]name specifies all mailslots on machines in the domain. In this case, the maximum message length is also 424 bytes.

Finally, the client must specify the FILE_SHARE_READ flag.

The functions GetMailslotInfo and SetMailslotInfo are similar to their named pipe counterparts.

Pipe and Mailslot Creation, Connection, and Naming

Table 11-1 summarizes the valid pipe names that can be used by application clients and servers. It also summarizes the functions to create and connect with named pipes.

Table 11-1 Named Pipes: Creating, Connecting, and Naming

image

Table 11-2 gives similar information for mailslots. Recall that the mailslot client (or server) may not be the same process or even on the same computer as the application client (or server).

Table 11-2 Mailslots: Creating, Connecting, and Naming

image

Example: A Server That Clients Can Locate

Program 11-4 shows the thread function that the command line server (Program 11-3), acting as a mailslot client, uses to broadcast its pipe name to waiting clients. There can be multiple servers with different characteristics and pipe names, and the clients obtain their names from the well-known mailslot name. Program 11-3 starts this function as a thread.

Note: In practice, many client/server systems invert the location logic used here. The alternative is to have the application client also act as the mailslot client and broadcast a message requesting a server to respond on a specified named pipe; the client determines the pipe name and includes that name in the message. The application server, acting as a mailslot server, then reads the request and creates a connection on the specified named pipe.

Program 11-4’s inverted logic solution has advantages, although it consumes a mailslot name:

• The latency time to discover a server decreases because there is no need to wait for a server to broadcast its name.

• Network bandwidth and CPU cycles are used only as required when a client needs to discover a server.

Program 11-5 shows the LocSrver function called by the client (see Program 11-2) so that it can locate the server.

Summary

Windows pipes and mailslots, which are accessed with file I/O operations, provide stream-oriented interprocess and networked communication. The examples show how to pipe data from one process to another and a simple, multithreaded client/server system. Pipes also provide another thread synchronization method because a reading thread blocks until another thread writes to the pipe.

Looking Ahead

Chapter 12 shows how to use industry-standard, rather than Windows proprietary, interprocess and networking communication. The same client/server system, with some server enhancements, will be rewritten to use the standard methods.

Exercises

11–1. Carry out experiments to determine the accuracy of the performance advantages cited for TransactNamedPipe. You will need to make some changes to the server code as given. Also compare the results with the current implementation.

11–2. Use the JobShell program from Chapter 6 to start the server and several clients, where each client is created using the “detached” option. Eventually, shut down the server by sending a console control event through the kill command. Can you suggest any improvements to the serverNP shutdown logic so that a connected server thread can test the shutdown flag while blocked waiting for a client request? Hint: Create a read thread similar to the connection thread.

11–3. Enhance the server so that the name of its named pipe is an argument on the command line. Bring up multiple server processes with different pipe names using the job management programs in Chapter 6. Verify that multiple clients simultaneously access this multiprocess server system.

11–4. Run the client and server on different systems to confirm correct network operation. Modify SrvrBcst (Program 11-4) so that it includes the server machine name in the named pipe. Also, modify the mailslot name, currently hard-coded in Program 11-4, so that the name is taken from the mailslot response from the application server.

11–5. Modify the server so that you measure the server’s utilization. (In other words, what percentage of elapsed time is spent in the server?) Maintain performance information and report this information to the client on request. Consider using the Request.Command field to hold the information.

11–6. Enhance the server location programs so that the client will find the server with the lowest utilization rate.

11–7. serverNP is designed to run indefinitely as a server, allowing clients to connect, obtain services, and disconnect. When a client disconnects, it is important for the server to free all associated resources, such as memory, file handles, and thread handles. Any remaining resource leaks will ultimately exhaust computer resources, causing the server to fail, and before failure there will probably be significant performance degradation. Carefully examine serverNP to ensure that there are no resource leaks, and, if you find any, fix them. (Also, please inform the author using the e-mail address in the preface.) Note: Resource leaks are a common and serious defect in many production systems. No “industry-strength” quality assurance effort is complete if it has not addressed this issue.

11–8. Extended exercise: Synchronization objects can synchronize threads in different processes on the same machine, but they cannot synchronize threads running in processes on different machines. Use named pipes and mailslots to create emulated mutexes, events, and semaphores to overcome this limitation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset