; LANTEST.TXT -- Documentation for LANTEST
; Copyright (c) 1994 by Allen Brunson  version 1.00  06/30/94


******************************************************************************
*                                                                            *
***  Trademarks                                                            ***
*                                                                            *
******************************************************************************

Borland is a registered trademark of Borland International, Inc.

Microsoft and MS-DOS are registered trademarks and Windows is a trademark
of Microsoft Corporation.

Netware and Novell are registered trademarks and IPX is a trademark
of Novell, Inc.



******************************************************************************
*                                                                            *
***  Introduction                                                          ***
*                                                                            *
******************************************************************************

LANTEST is a fairly basic program that nonetheless demonstrates all the
procedures necessary to make LANLIB work effectively.  It allows up to 30
users at PCs on a network to exchange messages in real-time (but could be
made to support a more or less unlimited number by changing the define
USERTOTAL in LANTEST.H).  It displays a large quantity of messages about its
operation so as to give the potential NETLIB user a feel for what is going
on.  It is far from bulletproof; a great many optimizations and "sanity
checks" that would have gone into a production program have been omitted to
keep the source code relatively simple.



******************************************************************************
*                                                                            *
***  Compiling LANTEST                                                     ***
*                                                                            *
******************************************************************************

The following source files pertain solely to LANTEST:

 LANTEST.C    Main LANTEST source file
 LANTEST.H    Defines for LANTEST
   INPUT.C    Keyboard input and input processing routines
 NETWORK.C    High-level IPX routines
 PROCESS.C    Miscellaneous processing routines
   VIDEO.C    Rudimentary screen functions

Like any other LANLIB-using program, LANTEST uses the following files:

LANLIBSC.LIB  LANLIB routines for the Small and Compact memory models
  LANLIB.H    Defines for LANLIB
  LANCFG.H    Configuration parameters for LANLIB
 LANUTIL.C    Optional routines for dealing with IPXADDRFULL structures

Create a project or make file that includes all the .C files and the proper
.LIB file.  LANTEST was designed as a small model program, but can be made to
work in any memory model (well, almost any memory model -- see below) by
including a different .LIB file and changing the memory model check near the
top of LANTEST.H.

LANLIB and LANTEST have been tested with Borland's Turbo C++ v1.0 and v3.0
and Microsoft's C++ v8.0.  They work fine with all of these, except that the
Tiny model doesn't work with Microsoft C; its linker says that far segment
accesses are not allowed in that model.

While tweaking the program to work with Microsoft C, I couldn't find library
functions to do certain things I wanted to do, so rather than writing my
own functions, I just left those parts out.  Specifically, when compiled
with a Microsoft compiler, LANTEST will not be able to scroll the upper
part of the screen (it simply erases it instead), it won't wait a variable
length of time before sending ping responses, and it is not able to set
screen colors.

An already compiled version of the demo is given as LANTEST.EXE.



******************************************************************************
*                                                                            *
***  Running LANTEST                                                       ***
*                                                                            *
******************************************************************************

LANTEST should be run on two or more PCs, but will minimally function on only
one.  If you have the full version of LANLIB, the PCs involved can be on
any network segments in an internetwork; if you have the shareware version,
then the PCs must be on the same segment.

Each PC must at minimum have the IPX TSRs loaded; it doesn't matter whether
NETX or VLM is loaded or not.  If you do have a "full" network, including a
NetWare server, it might be easiest to copy LANTEST.EXE into a subdirectory
on the server and run it from there.  On the other hand, you might wish to
copy LANTEST.EXE to each individual PC's hard disk and unload NETX or VLM,
just to convince yourself that LANLIB really does work without a server.

When started, LANTEST draws a box around most of the screen, leaving a single
line free at the bottom for entering commands.  If your display is in a text
mode that has more than 25 lines, LANTEST will adjust itself accordingly;
since it can display up to several lines of information for each packet
transaction, it needs all the lines it can get.  The utility LINE50.COM is
included to put VGA displays into 50-line mode (or EGAs into 43-line mode);
you can run this before LANTEST if you wish.

LANTEST initializes LANLIB with a call to ipxStart().  If the IPX driver
isn't loaded, you'll get an error message to that effect; the program can't
continue.  If the IPX driver was located in memory, LANTEST will display the
local PC's network address, a reminder that F1 will produce a help screen,
and instructions to use the NAME and PING commands to get started (or NAME,
ROUTE, and PING, if you've got the full-blown version).

You can press F1 or enter the command HELP (or H) at any time to get a list
of commands and keys recognized.

Enter "NAME Allen" (without the quotes), replacing my name with yours.
The program will tell you that it has set your name.

If you've got a registered version of LANLIB that includes the router-finder,
next type ROUTE to update the program's network table.  This might take
awhile; you can press Esc to abort.  After the route-find is complete, you
can type NET to get a list of all networks that were discovered.  If you
enter a filename after NET, then it will also write the information to a
file.

Next enter PING, which will cause LANTEST to broadcast an "are you there?"
request to all potential users.  (If you do the PING before ROUTE, then
LANTEST will only know about its own network segment at that point and
therefore will only ping locally.)  Other copies of LANTEST will respond with
"Yes, I'm here" packets, sending the name of the person along with the
message.  LANTEST waits for a random amount of time up to one second before
sending "Yes, I'm here" responses to keep the copy of the program doing the
asking from being deluged with replies all arriving at the exact same
instant.

You should get messages onscreen indicating received ping responses.  Wait
at least a second for the activity to die down.  Then enter DISPLAY to
show a list of all users that were found and the IPX addresses of their PCs.
The numbers in the "Rcv" and "Snd" columns indicate the maximum number of
receive and send ECBs that each machine has ever used up to this point.
The number in the "Time" column is the estimated time it takes to send a
packet to that user, in IBM PC clock ticks (about 1/18th of a second); this
information is returned by the ipxAddrImmed() procedure.  The machine you are
using is always first in the list and indicated by an asterisk.

The simplest command you can use is BROADCAST [message], which sends a
message to all users that the program knows about.  (Despite the name, it
does NOT broadcast the message to address FFFFFFFFFFFFh; this is considered
a sloppy programming technique.)  Note that this has the effect of updating
all receivers' user lists with your name and ECB usage statistics, if their
information is out of date.

The MESSAGE [usernum] [message] command will send a message to only one user.
You must enter the user's number from the list generated by DISPLAY.

An interesting feature of LANTEST is that it will try to interpret the text
of a received broadcast message or directed message as if it were a command.
If it's not a valid command, no error will be displayed; it is assumed the
message was only informational.  If the message is a valid command, then
the receiving copy of LANTEST will act on it.  Therefore, all running copies
of LANTEST can be controlled from any one copy.

The FLURRY command is used to send out flurries of packets.  FLURRY ON will
begin the packet flurry.  If at least one copy of LANTEST is sending
flurries, then all PCs that it knows about will receive the flurries and
will display the total number of flurry packets received every time that
1,000 of them have arrived.  Try issuing the command BROADCAST FLURRY ON,
which will send the command to all PCs running LANTEST, and see how badly
your network bogs down.  FLURRY OFF turns off the flurry packets;
BROADCAST FLURRY OFF will instruct all PCs to stop.  FLURRY RESET resets
the number of received flurry packets to zero.

STAT displays the maximum number and total number of send and receive ECBs.
(The maximums are also displayed in the DISPLAY listing.)  You can reset
these values to zeroes with STAT RESET.

If one user exits LANTEST, it doesn't send any kind of notification to other
users that it has left, so their user tables will be out of date.  Issuing
another PING will clear things up.  This could have been taken care of by
the program itself, but this is only a demo, after all.

When you're finished playing with LANTEST, enter QUIT to return to DOS.



******************************************************************************
*                                                                            *
***  Dissecting LANTEST                                                    ***
*                                                                            *
******************************************************************************

There are several source files necessary for LANTEST, but almost all of them
involve typical program tasks like printing text to the screen, managing
and processing user input, and so on, and can be safely ignored by the
reader who merely wants to learn how to use LANLIB.

LANTEST.H contains defines for LANTEST, including the definition of the
USER structure that holds information on all known users, and struct
LANTESTPKT, which is the structure for the data in all packets that LANTEST
sends and receives.

VIDEO.C contains rudimentary screen functions.  It has routines to do tedious
video tasks like drawing the box around the main window, scrolling the top
portion of the screen, and so on.  All video output to the top window is done
via calls to message().

INPUT.C is responsible for reading keyboard input and processing command
lines once the user has finished entering them.  Two of its procedures,
getKeys() and cmdProcess(), are in the program's main loop.

PROCESS.C contains miscellaneous procedures not very interesting to the
potential LANLIB user.  demoStart() checks to make sure that the DOS version
is at least 3.0, the minimum required for LANLIB.  The one IPX-related
procedure it contains is err(), a routine that will display a message
onscreen for each possible LANLIB error code.

NETWORK.C is the real heart of LANTEST.  For the reader interested in
learning how to use LANLIB, this is the file that deserves closest scrutiny.
It is initialized with a call to nStart(), which calls ipxStart() to start
up LANLIB.  If the define DEBUG in LANTEST.H is un-commented, then nStart()
passes the address of an IPXDATA structure, ipxData, to ipxStart() to use as
the communication data.  This is useful for debugging, as you can inspect the
many fields of the IPX structures to see what is going on.  If DEBUG is not
defined, then nStart() instead calls malloc() to allocate a block of memory
to use for communication data.

Next nStart() calls ipxAddrLocal() to get the address of the PC it is running
on and saves the address as the first entry in its user structure.  Then it
copies its address to ipxAddrBroad, an IPXADDRFULL, and uses the utility
routine ipxAddrBrd() to set the node and immediate address fields to
FFFFFFFFFFFFh, which makes the address suitable for broadcasting on its local
segment.  (This step is technically only necessary for single-segment
programs, but even the router-finding version of LANTEST is restricted to
a single segment until ipxRouteFind() is called.)

Here is LANTEST's main() procedure, from LANTEST.C:

byte main(void)                                    // Begin main()
  {
    demoStart();                                   // Start up subsystems

    while (!endProgram)                            // Main program loop
      {
        getKeys();                                 // Get input keys
        cmdProcess();                              // Process commands
        while (ipxRecvChk()) recvPacket();         // Process packets
        sendFlurry();                              // Send flurry packets
        sendErr();                                 // Check for send errors
      }

    demoStop();                                    // Stop subsystems

    return FALSE;                                  // Return errorlevel 0
  }                                                // End main()

It merely calls demoStart(), then calls six major procedures in a loop until
endProgram is TRUE, then calls demoStop().

The procedure getKeys() processes no more than one key at a time.  This
allows other tasks to run while the user is entering command lines.  The
procedure cmdProcess() will return immediately unless the user has just
pressed Enter, ending a command line, or unless there is a message to be
interpreted as a command.  If a command line is ready, cmdProcess() will
parse it and call the procedure necessary to execute it.

The procedure recvPacket(), in NETWORK.C, processes one received packet.
Note that the main loop is written so as to call recvPacket() as many
times as necessary to clear out all received packets.  This slows down
other processes somewhat but ensures that the receive ECBs should almost
never completely fill up.

recvPacket() checks the first word in the packet's data, looking for
LANTEST's signature.  If it doesn't find it, it displays an error message and
does nothing further with the packet.

If the signature is okay, it checks the packet type (the second word in the
packet) to see if it's a flurry packet.  If it is, it increments the count
of flurry packets.  If the count is evenly divisible by 1,000, it displays
the flurry packet total.

If it's not a flurry packet, it displays a message saying that it got the
packet, along with the address of the sender.

Since the signature in the packet was correct, the sender has now been
verified as a bona-fide LANTEST user, so userSave() is called to add this
user to the user table (or to update existing information).  The user's
address, name, and statistics are saved.

LANTEST uses only five types of packets: ping, ping response, broadcast,
message, and flurry.  A number indicating the packet type is stored in
the packet as its second word-sized variable.  Based on this information,
recvPacket() calls one of four routines to process the packet in some way
(flurry packets will have already been dealt with).

If the packet was a broadcast or message, then recvBroadcast() or
recvMessage() is called, respectively.  These packets are quite easy to deal
with; these procedures simply print "Broadcast from <name>:" or "Message from
<name>:" and then the text of the message, which is stored as the fourth and
final field in the data packet.  Then the text of the message is copied to
cmdStr, the command string, and inputFlag is set to 2, which means "remote
input received."  The next time through the main loop cmdProcess() will
notice this and try to interpret the message as a command.

Receiving a ping packet requires the most processing and is done by
recvPing().  Since the ping will be received by possibly a great many other
copies of LANTEST, all of which are going to send back a ping response, the
sender could be deluged with packets, its receive buffers might fill up
before it could process all the replies, and so some replies might be lost.
To keep this from happening, recvPing() waits for a random amount of time, up
to a second, before calling ipxSendPkt() to send a reply to the address that
the ping was received from.  Note that when LANTEST sends a ping, it receives
a copy just the same as everybody else, so this procedure checks to see if
the sending address is the same as this PC; if so, it doesn't send a ping
response.

If the packet received is a ping response, recvPingResponse() is called.
The purpose of a ping response is to make note of the sender in the user
table, and recvPacket() has already done this, so recvPingResponse() does
nothing but print a message onscreen.

routeFind() is called when the user enters the ROUTE command.  It calls
ipxRouteFind() to find networks, and uses routeWait() as its procedure to
be called while waiting for ipxRouteFind() to complete.  If DEBUG is defined,
it uses a static structure for the network info; if not, it uses malloc() to
get a block of memory instead.  The information that it gathers is only
used by sendPing(), described below.

The procedure sendFlurry() is called in the main loop.  If flurry mode isn't
turned on, it does nothing.  If flurry mode IS on, it sends one (but ONLY
one) flurry packet to a user in the user table.  Then it increments its
user number for the next call.  If the current user number points to a
slot in the user table that is unused, sendFlurry() does nothing; given
that most of the slots in the 30-user table will be probably be free,
sendFlurry() wastes a lot of time.  This was done intentionally.

The procedure sendErr() is called repeatedly in LANTEST's main loop.  It
checks for send errors with ipxSendChk(); if there is one, it uses
ipxSendErr() to retrieve the packet that couldn't be sent and the address it
should have been sent to.  If there's an error, sendErr() does nothing
but print a message; a "real" program should either retry the send or take
other action as necessary.

sendBroadcast() is used to broadcast messages to all users.  It does NOT
do so by sending the message to node address FFFFFFFFFFFFh, as you might
guess; it sends the message to each user in its user table instead.  This
is more work -- it has to do a send for every user in the table, instead of
just one send to its "broadcast" address (or a send to all of its broadcast
addresses, if the router finder has been called) -- but this is the proper
way to go about it.  IPX broadcasts are received by every PC on the network,
whether they're running your program or not.  Therefore, each and every IPX
driver on every machine on the network will receive a broadcast and have to
spend precious CPU cycles determining that there isn't a program running at
that machine that wants it.  Therefore, you should only use broadcasts when
absolutely necessary, which is normally only while you're looking for other
PCs running your program.  If you fail to follow this rule, the network
managers at large sites where your program is run will be very unhappy with
you.

Since sendBroadcast() does a bunch of sends in quick succession, it really
should be written so that it checks the return value from ipxSendPkt() to
see if all available send ECB/packet pairs are in use, and if so, it should
wait a short period of time, say 20 milliseconds, perhaps using the time
to call ipxSendErr() to clear out any errors, and then try again.  It doesn't
do this as written.

sendMessage() is pretty much like sendBroadcast().  It's a little simpler
in that it only has to send to one user, and a little more complicated in
that it has to collect a user number and check to make sure that the number
is valid.

sendPing() is called when the user issues the PING command.  It clears its
user tables of all entries (except the PC it is running on, of course) and
then broadcasts a ping packet.  A "ping packet," for the purposes of this
program, is nothing but one that has its packet type set to the numeric
value that means "ping packet."  Broadcasts should be avoided wherever
possible, because every PC on a network segment will receive them, and the
IPX driver at each PC will have to process them whether that PC has a running
program that wants the packet or not.  In this case a broadcast must be used,
since at this point it is not known who the other users are, so they can't
be targeted individually.  If the router finder is installed and it has been
called, then sendPing() will broadcast to all known networks; if not, then
it will broadcast only on its local network.



******************************************************************************
*                                                                            *
***  Thoughts on Robust IPX Programs                                       ***
*                                                                            *
******************************************************************************

There are a lot of things that can go wrong when using IPX.  You really
should take these things into account when designing an IPX-using program.

Among the most serious of IPX programming issues, and one that LANTEST
doesn't tackle at all, is the fact that IPX doesn't guarantee delivery.
In the context of LANTEST, a lost packet could mean that a broadcast message
doesn't arrive at all users' PCs, or that a ping response might be lost and
so someone's user table would be incorrect.  This isn't very serious in a
demo, but it could be quite disastrous in some production programs.

To make the process of keeping track of users more bulletproof, it would be a
good idea to have the program send a message to all users just before it
terminates.  Then they could all take that user out of their user table
without having to re-ping to clear things up.

When pinging, it would be advisable to broadcast a ping, wait a second or two
to receive replies, and then broadcast a second and third ping.  Given IPX's
claimed delivery rate of 95 percent, the odds against missing somebody that
way are quite remote.

To go even further, a ping probably shouldn't first clear out the user table,
but instead mark everyone as "suspect."  When a reply is received from
someone, that makes them a user in good standing.  If one or more PCs in the
table still haven't been heard from at the end of three pings, it's a good
bet that the user in some way exited the program without its being able to
send a message that it was terminating.  An error message stating that a
certain user has been "lost" might be appropriate.

When sending messages, it would be a good idea for the receiver to send back
a reply saying, "yes, I got it," perhaps also sending a checksum of the
message.  If the sender doesn't get a reply in a reasonable amount of time,
within a second or so, then it can display an error message stating that
the message wasn't received or try again.  The amount of time to wait before
timing out depends on the network itself.

Another serious issue is the fact that IPX packets may not arrive in the
same order you sent them in.  They probably will, especially on small
networks, but in larger networks there may be more than one route between
any two PCs, and based on network traffic or other criteria, a router might
send a packet one direction one time and another direction the second time;
this can certainly lead to out-of-order packets.

Therefore, if you're sending a message that's comprised of more than one
packet, all packets should contain some kind of sequence number.  That way
the receiver can reconstruct the message as necessary.

Network traffic can vary greatly from network to network and from minute
to minute.  Yours will almost certainly not be the only program running
on the network.  As you are writing your program, if at all possible take
into account the fact that network throughput will not remain constant, and
keep network traffic to a minimum as much as possible.

Some sort of handshaking will be necessary to get faster machines to
properly communicate with slower machines.  Chances are excellent that a
fast '486 will be able to send enough packets to a slow '286 to overrun its
receive ECBs very quickly.



******************************************************************************
*                                                                            *
***  Configuring LANLIB for LANTEST                                        ***
*                                                                            *
******************************************************************************

LANLIB is configured for a specific program via defines in LANCFG.H.  The
defines were not changed to reflect the needs of LANTEST because I wanted to
leave them at reasonable values for most programs.  However, it will be
instructional to consider how these values should have been set for this
program.

The value of IPXSOCKET should of course be set to some semi-random value
between 4000h and 7FFFh, just as with any other IPX-using program.  The user
wishing to make her program particularly bulletproof would set the socket
number in a configuration file so it could be changed if there is a conflict.

The value IPXNETCNT should be set to something small, since this is a demo; 6
would probably be a good value.

IPXDATASIZE is an easy one.  The largest packet that LANTEST ever sends
contains 92 data bytes, when doing a message broadcast or a message send
(ping packets and responses and flurries are smaller), so IPXDATASIZE
should be set to 92.

IPXRECVCNT should be set to a fairly large number, since flurries can
obviously fill up receive ECBs very quickly, especially when fast machines
send to slower ones.  Since the size of each individual packet buffer is very
small, and there's no performance penalty imposed by having lots of
ECB/packet pairs, this number should be set very high, probably all the
way up to the maximum, 250.

LANTEST only generates multiple sends under one circumstance: when
broadcasting a message to all users.  So the program might have to do as
many as 30 back-to-back sends.  But it's a fair bet that most sends will
complete within microseconds of making the request, so setting IPXSENDCNT
to 10 should be high enough.
