; 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 :" or "Message from :" 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.