Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Network Communications Using the NetBEUI Protocol


OCT94: Network Communications Using the NetBEUI Protocol

Network Communications Using the NetBEUI Protocol

Named pipes and mailslots for NT and Chicago

Marshall Brain

Marshall works for Interface Technologies (Wake Forest, NC), which does software design, consulting, and programmer training in Windows NT, Motif, C++, and object-oriented design. He is the lead author for Prentice Hall's five-book series on Windows NT, which includes Win32 System Services: The Heart of Windows NT. He can be reached at [email protected].


This article describes the NetBEUI (NetBIOS Extended User Interface) protocol and shows how to apply it in your own applications. NetBEUI is the native network protocol for Windows NT Version 3.1 and allows the operating system to communicate with other NT machines, as well as machines running Windows for Workgroups. NT uses NetBEUI internally to handle such things as disk and printer sharing over the network and also supports NetBEUI directly in the Win32 API, so that application developers can employ network communications in their own programs. Chicago, Microsoft's successor to Windows 3.1, will use the same NetBEUI interface and function calls in its API.

Two different facilities in the API, mailslots and named pipes, support NetBEUI network communications. I'll discuss both techniques in this article as well as their respective strengths and weaknesses. Because the Win32 API directly supports NetBEUI, it is remarkably easy to create applications that use the network in many different ways. For example, you might want to create a multiuser conferencing system for your network, similar to the "CB" systems you find on CompuServe and other BBSs. In a system like this, users run the conferencing program on their machines, and any messages they type get broadcast to all of the other users on the network. You generally use mailslots for the implementation because mailslots make it easy to broadcast information. Any multiplayer game that uses the network employs similar techniques.

When you want to stream large quantities of data between two machines, you normally use point-to-point named-pipe connections. For example, you would use named pipes for a digitized phone or video system implemented on a network. Any client/server configuration also uses named pipes. One central machine acts as the server, and then all of the clients connect to it individually with named pipes; the server uses a multithreaded approach to handle multiple connections simultaneously.

Network Basics

Figure 1 illustrates a simple network that you might find in a small business. Each machine has a network adapter that connects it to the network, as well as a name that uniquely identifies it. The network adapter determines the type of network, generally either Ethernet or Token Ring. The adapter also controls the media used for the network: coax or twisted pair, for example. In this kind of simple network, all machines can communicate with all others equally.

Machines can communicate using NetBEUI via either mailslots or named pipes. With a mailslot, one machine can broadcast a message that is received by all of the other machines on the network. With a named pipe, one machine chooses another and forms a specific connection to it. The advantage of a named pipe is that the connection is reliable. If the connection breaks--because a network card or cable malfunctions, for example--both ends of the connection receive notification of the break immediately. Mailslots are unreliable in the sense that the sender has no way to confirm receipt of messages. The advantage of a mailslot is that it is easy to get information to many machines simultaneously.

Figure 1 shows one network segment. A segment is defined as a group of machines directly connected to one another. There is a limit to the number of machines that can exist on one segment, however, because network traffic grows with the number of machines. Generally, the limit is about 30 machines. In a large company, each department might have a single segment consisting of 20 to 30 machines. All of the segments are then connected to one another with a router so that they can intercommunicate, as shown in Figure 2. This distinction is important because, in general, NetBEUI messages are not routable.

Using mailslots and named pipes, three different communication architectures are possible: broadcast, point-to-point, and client/server. In broadcast mode using a mailslot, one machine sends a message to all others on the segment. In point-to-point communications, one machine forms a specific connection with another and data passes back and forth using a named pipe. In a client/server relationship, one machine acts as the server, and all clients connect to it with point-to-point named-pipe connections. To emulate a broadcast operation with a client/server architecture, one machine sends a message to the server, and then the server sends duplicates of the message individually to each client.

Mailslot Connections

Mailslots are the simplest way to perform network communications in the Win32 API. Mailslots provide a one-directional communication path from a sender to one or more recipients on the same network segment. You generally use mailslots when you want to send data to many recipients at once.

Mailslots are extremely easy to create, and reading and writing are done using the API's normal ReadFile and WriteFile functions. When creating the mailslot, a special pathname passed to CreateMailslot causes the system to create a mailslot rather than a normal file. The details of this call are found in the Win32 programmer reference.

The programs shown in Listings One and Two are as simple as possible so that you can easily see the steps necessary to transmit and receive data through a mailslot. Listing One is sms_recv, a program that creates and reads from a mailslot server using polling. The code shows how to create a mailslot server with CreateMailslot. The server is a queue that holds messages received until the user reads them using ReadFile. Messages are stored in the queue in the order of their arrival.

The name of the mailslot must be of the form \\.\mailslot\[path]name. This looks just like a filename, and it acts like a filename in ReadFile. However, no actual file is created by the function; the mailslot is simply held in memory. A typical mailslot name, \\.\mailslot\sms, is used in Listing One. It is also possible to add subdirectories to the path to further categorize mailslots.

When you create the mailslot, you can specify the maximum message length, as well as the read timeout. Mailslots can send no more than 400 bytes over the network in a single message. If you set the timeout value to 0, then any call to ReadFile will return immediately whether or not there is anything in the buffer. If you set the timeout value to a specific number of milliseconds, any read operation will fail if that amount of time elapses before a message arrives. You can also use the MAILSLOT_WAIT_FOREVER constant to create a blocking read.

The sms_recv program takes the non-blocking approach and uses GetMailslotInfo to make sure that messages exist in the mailslot queue before performing a read. This function returns the maximum length of messages in the queue, the length of the next message in the queue, and the number of messages waiting. The code continuously checks to determine if messages exist in the mailslot. If so, then it reads the first one. Reading from a mailslot is just like reading from a file.

Any message sent from any computer on the network to a machine running sms_recv will be received, provided that the mailslot names of the sender and the receiver match. The program sms_send (Listing Two) shows how to send messages to a mailslot; it writes to a mailslot every five seconds. It starts by using the normal CreateFile to open a writable connection to the mailslot. The program is referred to as a "mailslot client" because it writes to mailslot servers already running on the network. CreateFile understands, via the special mailslot filename, that you are not creating a file but instead wish to communicate with a mailslot. Four different formats for the filename are possible. The first is:\\.\mailslot\[path]name. In this case, the name specifies the local machine. Alternatively, you can identify a specific machine on the net via the secondformat\\machine\mailslot\[path]name. To specify a broadcast operation to all machines in the local machine's primary domain, you use the format: \\*\mailslot\[path]name. Finally, to all machines in an indicated domain, you would use: \\domain\mailslot\[path]name. For more information on domains and domain controllers, refer to Windows NT Administration: From Single Machines to Heterogeneous Networks (Prentice-Hall, 1994).

After opening the mailslot, sms_send gets the local computer's name using GetComputerName, and then broadcasts the name to all mailslots in the current domain every five seconds.

The sms_recv program checks for messages via polling. Every half-second it calls GetMailslotInfo and checks to see if any messages are waiting in the slot. In general, polling is not a good technique for a multithreaded environment because it is inefficient. You can eliminate polling by setting the timeout value in CreateMailslot to an appropriate value and then calling ReadFile with a buffer length of zero to wait for a message to arrive. Once this call to ReadFile returns, you know a message exists, so you can then call GetMailslotInfo and ReadFile, as in Listing One.

When you run sms_send, it broadcasts to all machines on the network. If you run multiple copies of the reader on the same or different machines, all of them will see the messages produced by the writer. Alternatively, you can run multiple writers on the net, and any copies of the reader will see the messages from all of them. In both Listing One and Two, note the presumption that the program will be terminated externally. You can formally close either a mailslot server or client using CloseHandle.

Named Pipes

Named pipes provide a guaranteed delivery mechanism. Instead of broadcasting the packet onto the network, you form a distinct connection to another machine with a named pipe. If the connection breaks, both parties to the connection find out as soon as they try to send or receive anything. Packets are also guaranteed to arrive in sequence through a named pipe. The only problem with named pipes is that you lose the ability to broadcast packets. To broadcast anything, all of the target machines must have an individual connection to a central server, and the server must separately transmit the message to each one.

Named pipes are only slightly more difficult to create than mailslots. Listings Three and Four show how to create a simple, point-to-point connection between two applications using named pipes. The program ssnprecv (Listing Three) is a named-pipe server (receiver); its counterpart is ssnpsend (Listing Four), which connects to the receiver and sends it messages.

You can run both these programs on the same machine. Launch the receiving program first, and then on the same machine, run ssnpsend. It will query you for the name of the machine to connect to. Type "." or enter your machine name. You will see a message sent from sender to receiver every five seconds or so. When you kill off the sender, notice that you immediately see a message in the receiver indicating that it has detected the break in the pipe. If you try to start up the sender without the receiver running, the sender will fail immediately because it cannot connect. Unlike mailslots, pipes can tell when the other end is not working properly.

A named-pipe connection can occur across the network as simply as it occurs on the same machine. For example, if ssnprecv is running on a machine named "orion," you can log in to a different machine using an account with the exact same login ID and password as the one you are using on "orion." Run ssnpsend on the new machine and enter the name "orion" when it asks for the machine's name. The connection will occur properly. Note that, with named pipes, you must know the name of the machine running the server.

Also, note that if a different user tries to connect to the receiver, the connection fails. For example, when user "jones" is running the receiver on the machine "orion" and user "smith" tries to connect from another machine, the connection fails with an "access denied" error. This is the NT security system at work. See "Win32 System Services" for details on named-pipe security.

The ssnprecv program starts by creating a named-pipe server using CreateNamedPipe. The name used with CreateNamedPipe will always have the form \\.\pipe\[path]name.

As with mailslots, you can specify a path before the name of the pipe to clearly distinguish it from other pipes on the system. The openMode parameter passed to CreateNamedPipe lets you determine the direction of the pipe. Named pipes can be one directional or bidirectional, depending on certain #defined constants used with the openMode parameter. These constants are: PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, and PIPE_ACCESS_OUTBOUND.

The pipeMode parameter of CreateNamedPipe determines whether the pipe works with a pure stream of bytes or with packets of bytes, called "messages." A stream of bytes has no logical boundaries. Messages contain a group of bytes perceived as a unit. You can declare byte or message behavior in both the read and write directions, via the following constants: PIPE_TYPE_MESSAGE, PIPE_TYPE_BYTE, PIPE_READMODE_MESSAGE, and PIPE_READMODE_BYTE.

A pipe can have more than one instance on a single machine. This capability allows an application to handle multiple clients, each in different threads, and is required to create a named-pipe server. Because the sender/receiver pair in Listings Three and Four comprises a simple point-to-point connection where only one instance is necessary, a maximum of one instance is specified in the call to CreateNamedPipe. See "Win32 System Services" for details on server configurations.

The ssnprecv program next waits for a connection on the named pipe using ConnectNamedPipe. A connection is formed in the server when a client program calls CreateFile with the proper machine and named pipe specified as its destination. Upon connection, ConnectNamedPipe returns. Alternatively, you can specify an overlapped structure and ConnectNamedPipe will return immediately and later signal the event upon connection.

The server then enters a loop, waiting for data to arrive. ReadFile behaves slightly differently here than it does with files. Because this named pipe is in message mode, ReadFile will return as soon as it receives a complete message, regardless of how many bytes the message contains. It is possible to use a blocking read as shown, or an overlapped read.

The ssnpsend program in Listing Four is a simple client for ssnprecv. It starts by creating a connection to the named pipe via CreateFile. It then writes messages using WriteFile. Each individual call to WriteFile constitutes a message at the receiving end of the named pipe, so the receiver's ReadFile function will unblock when it receives the message. Each time the client writes, the server produces a message on the screen.

If two copies of the client try to connect to ssnprecv at the same time, then the server will reject the second client. If you terminate either the client or the server, then the other half of the pair will immediately terminate when it detects the broken connection.

Figure 1 A typical network.

Figure 2 Two segments connected by a router.

Listing One


//*******************************************************************
// sms_recv.cpp -- this creates a mailslot server and reads from it.
// The mailslot receiver uses polling.           By Marshall Brain.
//*******************************************************************
#include <windows.h>
#include <iostream.h>

int main()
{   char toDisptxt[80];
    HANDLE hSMS_Slot;
    DWORD nextSize,Msgs,NumBytesRead;
    BOOL Status;

    /* Create a mailslot for receiving messages */
    hSMS_Slot=CreateMailslot("\\\\.\\mailslot\\sms",
                              0, 0, (LPSECURITY_ATTRIBUTES) NULL);
    /* Check and see if the mailslot was created */

    if (hSMS_Slot == INVALID_HANDLE_VALUE)
    {   cerr << "ERROR: Unable to create mailslot " 
             << GetLastError() << endl;
        return (1);
    }
    /* Repeatedly check for messages until the program is terminated  */
    while(1)
    {   Status=GetMailslotInfo(hSMS_Slot,

                          (LPDWORD) NULL, &nextSize, &Msgs,(LPDWORD) NULL);
        if (!Status)
        {   cerr << "ERROR: Unable to get status. " 
                 << GetLastError() << endl;
            CloseHandle(hSMS_Slot);
            return (1);
        }
        if (Msgs)        /* If messages are available, then get them */
        {   
            /* Read the message and check if  read was successful */
            if (!ReadFile(hSMS_Slot, toDisptxt, nextSize,
                          &NumBytesRead, (LPOVERLAPPED) NULL))
            {   cerr << "ERROR: Unable to read from mailslot " 
                     << GetLastError() << endl;
                CloseHandle(hSMS_Slot);
                return (1);
            }
            cout << toDisptxt << endl;/* Display the Message */
        }
        else Sleep(500);  /* Check for new messages twice a second */
    } /* while */
}

Listing Two


//***************************************************************
// sms_send.c --  a simple mailslot sender that writes to
// a mailslot every five seconds.               By Marshall Brain.
//***************************************************************
#include <windows.h>
#include <iostream.h>
#include <string.h>

int main()
{   char toSendTxt[100], buffer[100];
    DWORD bufferLen=100, NumBytesWritten;
    HANDLE hSMS_Slot;
    BOOL Status;
    /* Create the mailslot file handle for sending messages  */
    hSMS_Slot=CreateFile("\\\\*\\mailslot\\sms",

                         GENERIC_WRITE, FILE_SHARE_READ,
                         (LPSECURITY_ATTRIBUTES) NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,(HANDLE)NULL);
    /* if the mailslot file was not opened, terminate program */
    if (hSMS_Slot == INVALID_HANDLE_VALUE)
    {   cerr << "ERROR: Unable to create mailslot "
             << GetLastError() << endl;
        return (1);
    }
    GetComputerName(buffer, &bufferLen);    /* form string to send */
    strcpy(toSendTxt, "Test string from ");
    strcat(toSendTxt, buffer);
    /* Repeatedly send message until program is terminated  */
    while(1)
    {   cout << "Sending..." << endl;
        /* Write message to mailslot */
        Status=WriteFile(hSMS_Slot, toSendTxt, (DWORD) strlen(toSendTxt)+1,
                                        &NumBytesWritten, (LPOVERLAPPED) NULL);
        /* If error occurs when writing to mailslot,terminate program */
        if (!Status)
        {   cerr << "ERROR: Unable to write to mailslot "
                 << GetLastError() << endl;
            CloseHandle(hSMS_Slot);
            return (1);
        }
        Sleep(4800);     /* Wait sending the message again */
    } /* while*/
}









Listing Three


//*********************************************************************
// ssnprecv.cpp --- a simple named pipe server (receiver). The 
// server will wait and accept one connection, then receive messages 
// from it. By Marshall Brain.
//*********************************************************************
#include <windows.h>
#include <iostream.h>

int main()
{   char toDisptxt[80];
    HANDLE ssnpPipe;
    DWORD NumBytesRead;

    /* Create a named pipe for receiving messages */
    ssnpPipe=CreateNamedPipe("\\\\.\\pipe\\ssnp",
                   PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_WAIT,

                           1, 0, 0, 150, (LPSECURITY_ATTRIBUTES) NULL);
    /* if named pipe was not created, terminate */
    if (ssnpPipe == INVALID_HANDLE_VALUE)
    {   cerr << "ERROR: Unable to create a named pipe. "
             << endl;
        return (1);
    }
    cout << "Waiting for connection... " << endl;
    /* Allow a client to connect to the pipe, terminate if unsuccessful */
    if(!ConnectNamedPipe(ssnpPipe, (LPOVERLAPPED) NULL))
    {   cerr << "ERROR: Unable to connect a named pipe "
              << GetLastError() << endl;
        CloseHandle(ssnpPipe);
        return (1);
    }
    /*Repeatedly check for messages until the program is terminated. */
    while(1)
    {   /* Read the message and check to see if read was successful */
        if (!ReadFile(ssnpPipe, toDisptxt, sizeof(toDisptxt),
                      &NumBytesRead, (LPOVERLAPPED) NULL))
        {   cerr << "ERROR: Unable to read from named pipe "
                 << GetLastError() << endl;
            CloseHandle(ssnpPipe);
            return (1);
        }
        cout << toDisptxt << endl;/* Display the Message */
    } /* while */
}



Listing Four


//***************************************************************
// ssnpsend.cpp -- a simple named pipe sender.
// This connects to receiver (ssnprecv) and sends it messages.
//***************************************************************
#include <windows.h>
#include <iostream.h>

int main()
{   char *toSendtxt="Test String";
    HANDLE ssnpPipe;
    DWORD NumBytesWritten;
    char machineName[80],pipeName[80];

    cout << "Enter name of server machine: ";
    cin  >> machineName;
    wsprintf(pipeName, "\\\\%s\\pipe\\ssnp", machineName);


    /* Create the named pipe file handle for sending  messages */
    ssnpPipe=CreateFile(pipeName,
                        GENERIC_WRITE, FILE_SHARE_READ,
                        (LPSECURITY_ATTRIBUTES) NULL,
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                        (HANDLE) NULL);
    /*  If the named pipe file was not  opened, terminate program */
    if (ssnpPipe == INVALID_HANDLE_VALUE)
    {   cerr << "ERROR: Unable to create a named pipe " << endl;
        cerr << GetLastError() << endl;
        return (1);
    }
    /* Repeatedly send message until program is terminated */
    while(1)
    {   cout << "Sending..." << endl;
        /* Write message to the pipe */
        if (!WriteFile(ssnpPipe,
                       toSendtxt, (DWORD) strlen(toSendtxt)+1,
                       &NumBytesWritten, (LPOVERLAPPED) NULL))
        {   /* If an error occurs when writing to pipe, terminate program */
            cerr << "ERROR: Unable to write to named pipe "
                 << GetLastError() << endl;
            CloseHandle(ssnpPipe);
            return (1);
        }
        Sleep(4800);/* Wait before sending the message again */
    } /* while*/
}

Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.