High-Speed File Transfers With Netbios

Costas shows you how to take advantage of the high-speed data transfer facilities LAN's provide, without having to bite the LAN bullet.


October 01, 1989
URL:http://www.drdobbs.com/parallel/high-speed-file-transfers-with-netbios/184408218

OCT89: HIGH-SPEED FILE TRANSFERS WITH NETBIOS

Costas is a senior software developer and part owner of The Software Bottling Company and a regular contributor to DDJ. He can be reached at 6600 Long Island Expressway, Maspeth, NY 11378. MCI Mail SBC. CompuServe: 72377, 1121.


If you regularly transfer large amounts of data between PCs, you're familiar with the time-consuming constraints of 1200-, 2400-, or even 9600-baud communications. No doubt you've wished more than once you could transfer files at speeds that local area network (LAN) users have become used to; speeds as (relatively) low as 2,500,000 bits/ sec. (2.5-Mbits/sec.) for ARCnet, to as high as 10-Mbits/sec. for Ethernet.

Considering that in many environments, LANs are getting to be as prevalent as hard disks, high-speed file exchange using LAN facilities is becoming increasingly common. But even if you don't have access to a LAN, it's still possible to use LAN adapter cards, which now sell for as low as $100 to $200 each, as a means of exchanging files at high speeds between two (or more) PCs. You don't even need the network server or operating system to do so. All you need are the cards, a cable, and a quasi-standard driver called NetBIOS, which is usually supplied or sold with most LAN adapter cards -- and the program I'll describe in this article.

The program, which I call XNet, is a custom file transfer program that uses the NetBIOS interface. The nice thing about the program is you don't have to concern yourself with the usual serial communications nuisances -- error corrections, checksums, baud rates, and such. NetBIOS does all this (and more) for you. The program doesn't even have to wait around to receive data because NetBIOS can notify your routine when there is data and let you know if a data transfer to another node failed. The beauty of all this is that data is transferred from 2.5-Mbits/sec. to 10-Mbits/ sec., depending on the type of adapter cards you're using.

The NetBIOS Interface

NetBIOS is usually loaded into DOS as a device driver from the CONFIG.SYS. Follow the board manufacturer's installation guidelines. Before calling NetBIOS, you must set the command and other parameters in the message control block (MCB). You then load the register pair ES:BX with the segment and offset address of the MCB. To call the NetBIOS, you execute an interrupt 5Ch. For a list of NetBIOS commands, see Figure 1; for the structure of the MCB, refer to Figure 2. When the interrupt completes, you check the mcb_retcode for a successful completion. If it is non-zero, it usually means there was a problem that your program must react to.

Figure 1: NetBIOS commands and values

  NetBIOS commands used in this program

  msg_reset=$32;     Reset the node
  msg_status=$33;    Determine the current state of the node
  msg_add_name=$30;  Add a 16 char unique node name to NetBIOS
  msg_listen=$11;    Listen for a node to establish session
  msg_call=$10;      Call another node to establish a session
  msg_hang_up=$12;    Hangup the session with a node
  msg_send=$14;      Send a block of data to a node
  msg_receive=$15;   Receive a block of data from a node

Other NetBIOS commands

  msg_add_group_name=$36;             Add a group name
  msg_cancel=$35;                     Cancel the last MCB command
  msg_chain_send=$17;                 Send 2 data buffers, one after
                                       another
  msg_delete_name=$31;                Delete a name from the adapter
  msg_find_name=$78;                  Find a a node name on the LAN
  msg_receive_any=$16;                Receive from any session partner
  msg_receive_broadcast_datagram=$23; Datagram from any node on LAN
  msg_receive_datagram=$21;           Datagram from a specific name/group
  msg_send_broadcast_datagram=$22;    Datagram to anyone on LAN
  msg_send_datagram=$20;              Datagram to specific name/group
  msg_session_status=$34;             Status of a session

Figure 2: NetBIOS message control block

  buffer=array[1..buffsize] of byte;  Buffer type declaration
  buffp=^buffer;                      Pointer type to the buffer
  arrname=array[1..16] of char;       Array for names type

  Message control block record

  mcb=record
    mcb_command: byte;                  Command to execute
    mcb_retcode: byte;                  Return code value
    mcb_lsn: byte;                      Local session #
    mcb_num: byte;                      Number of name added
    mcb_buffer: pointer;                Data buffer address
    mcb_length: word;                   Buffer length in bytes
    mcb_callname: arrname;              Name on remote node
    mcb_name: arrname;                  Name of local node
    mcb_rto: byte;                      Receive timeout (NOT USED)
    mcb_sto: byte;                      Send timeout (NOT USED)
    mcb_post: pointer;                  Post routine address (NOT USED)
    mcb_lana_num: byte;                 Adapter card to use.  0 is first
    mcb_cmd_cpl: byte;                  Command status if NOWAIT is used
    mcb_reserve: array[1..14] of byte;  Other detailed info
  end;

Although, not implemented in the XNet program, a feature that needs special attention must be explained. Most commands allow you to call with the no-wait bit (bit 8) set. This means that after you have executed the call to NetBIOS, you do not have to wait for the completion. Rather, NetBIOS notifies you by calling a designated routine called a "post routine." The post is an interrupt-like function that you write, and whose address you set in the mcb_post field of the MCB. When your routine is finally called, your program then checks the MCB for errors, or any other relevant fields. This is a powerful feature, for without it, most network software would not work so elegantly.

MCB Explained

The MCB fields are set by your program before calling the NetBIOS and are set again by NetBIOS after the call. You may have many independent MCBs as long as you point ES:BX to the right one before calling NetBIOS. Not all commands use all the fields.

The first and most important field is the mcb_command. Here, you set the code for the command you wish to execute (see Figure 1). The next field, mcb_retcode, is where the completion return code is set by NetBIOS. It must be set to FFh before calling NetBIOS. The session number mcb_lsn is returned to your program by the NetBIOS after a successful connection with another node on the system. You use this for all interactions with that particular node. The number mcb_num identifies a local node name in the adapter card, while mcb_buffer is a pointer to the data buffer for receiving or sending blocks of data. The mcb_length determines the length of the data in the mcb_buffer, mcb_callname, and mcb_name are the names used when calling or listening for another node. The send and receive time-out values are mcb_rto and mcb_sto. If they are set to zero, there will never be a data transmission time-out because of a problem or delay on the network due to other traffic. The mcb_post field is set to point to our interrupt routine handler when we call NetBIOS with the no-wait in the command field bit set. The LAN card number, usually set to 0, is mcb_lana_num, and mcb_cmd_cpl is a command completion code when we call NetBIOS with the no-wait bit in the command field set. The mcb_reserve field is used to fill in information by some NetBIOS commands.

NetBIOS Commands

As you can see from Figure 1, the NetBIOS has quite an assortment of commands. For now, I'll explain only the commands used in the XNet program.

The first command is the msg_reset, which resets the LAN adapter card and clears out any node names it may have been using. You should only use this command if no other software is using the LAN card at the time. The command msg_status returns the LAN card's node address, a unique number between 1 and 255, depending upon the installation of your LAN adapter. No two adapters can have the same node number. The msg_add_name command allows your node to be known by some arbitrary, unique name on the network. Your node can have multiple names, but no two nodes can have the same name. The msg_listen command makes your program wait for someone to talk to you and is the opposite of msg_call. Together they use each other's names to establish a session. The commands msg_send and msg_receive send and receive data via mcb_buffer and msg_hang_up, which hang up any session with another node.

Your program could be communicating with many nodes at once, provided you have established a unique session with them. Each session has its own number that is used for this purpose.

The XNet File Transfer Program

Even though I chose to write XNet in Turbo Pascal (see Listing One), you can use C, or for time/space critical functions, assembler. The program is as simple as you can get for transferring data from one node to the other. I did not use the no-wait capability; the program must be run simultaneously on each system (run it on node A first, and then run it on node B).

I programmed each NetBIOS function into a procedure call passing the required parameters. All the procedures start with net_. The procedure init_mcb is called to set the MCB to a known state. Then each one of the commands sets the calling parameters before executing an interrupt 5Ch.

Notice that net_reset and net_status check if the NetBIOS driver is installed. This is done by checking if the 5Ch vector value is set to a non-zero value. Any errors during a call to NetBIOS will cause a message indicating the command and error code (in hex) to be displayed on the screen. The program then terminates by calling the terminate function.

Although XNet should work with any adapter board configuration that supports NetBIOS, I tested it on PC210 ARCnet boards from Standard Microsystem Corp. (Long Island, N.Y.) using their NetBIOS.

My CONFIG.SYS file had the following line which tells NetBIOS the port, the IRQ, and the memory buffer for the board:

  DEVICE=SMCARC.SYS /P2E0 /I2 /ME000.

To compile the program, simply compile to disk. To run the EXE file, type XNET at the DOS prompt.

Start the program on whatever nodes will be talking to each other. You'll first see your node's number on the screen, and then you'll be prompted for the remote node's number (a number between 1 and 255).

You are now ready to receive on one station and send on the other. On station A, select Receive and give the file name to save into. On station B, select Send and give the file name to send. As soon as you give the file name to send, the program will transmit the file, showing in bytes the size of the file transferred. For small files (around 128K), the transfer is practically instantaneous. In cases of a message specifying the error code, the command code the error occurred on will appear.

Program Flow

When the program is started up, it will first call the net_reset procedure. This initializes the board and checks that you have NetBIOS running. After the reset, the program calls net_status to get the node's number. This is the first byte of the data pointed to by mcb_buffer. For convenience, I use this number as the name of the node by converting it to a string and calling net_add_name. For the other node to communicate, it must use this name.

The program will then prompt for the remote node's number which must be different from the local. It will then ask if you wish to Send, Receive, or Exit. If you choose Exit, the program terminates by calling the procedure Terminate. If you choose Send, it will call the procedure setup_call_send, and for Receive, it will call setup_listen_receive.

The procedure setup_listen_receive asks for the file name to save to. If the file exists, it will ask you to overwrite it. It will then proceed to listen for a call from the remote node using the function net_listen, and wait indefinitely.

The setup_call_send procedure asks for the file name to send, verifies its existence, and calls the remote node using the function net_call. If the remote node is not listening, it prints a message to Retry or Abort. If you abort, the program terminates; otherwise, it reexecutes the net_call.

Once the two nodes establish a session via the above description, a session number is returned by NetBIOS. This session number is then used to send the file and receive it on the other end. The procedures send_the_file and receive_the_file will perform this function.

The file is sent/received using net_send and net_receive, respectively. The first send is a 4-byte (2 word) file size number that tells the receiver how big a file to expect. The sender will send the file in chunks of 64K bytes with the last chunk being the remainder of the 64K. As soon as the file size is reached, they both terminate by calling terminate. If there is a disk error (such as disk full) during the receive, the program terminates by hanging up -- using net_hang_up and calling the terminate function.

Technical References

Standard Microsystems Corp., Long Island, N.Y. ARCNET Installation Guide.

IBM, Token-Ring Network PC Adapter, Technical Reference Manual.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063, or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

_High-Speed File Transfers With NetBios_ by Costas Menico

[LISTING ONE]




program xnet;
{
  Program to demonstrate file transfer between PCs
  using the NETBIOS device driver.  This program should work with
  any hardware and software that support the NETBIOS interface.
  Network software other than the NETBIOS is not required.
  Program was tested with the PC260 Arcnet boards from SMC. The
  CONFIG.SYS had the following line to install the NETBIOS:
     device=smcarc.sys /p2e0 /i2 /me000
  Program author: Costas Menico
}

{$I-,R-}
uses dos, crt;

const
  { Maximum # of bytes to transfer in a single send }
  buffsize = 64*1024-1;
  lancard=0;    { Default network card }
  nowait = $80; { Return immediately from command.
                  Call the POST routine when done. (NOT USED)}
  wait=$0;      { Wait until command is done. }

  { NETBIOS Commands used in this program }
  msg_reset=$32;    { Reset the node }
  msg_status=$33;   { Determine the current state of the node }
  msg_add_name=$30; { Add a 16 char unique node name to NETBIOS }
  msg_listen=$11;   { Listen for a node to establish session }
  msg_call=$10;     { Call another node to establish a session }
  msg_hang_up=$12;  { Hangup the session with a node }
  msg_send=$14;     { Send a block of data to a node }
  msg_receive=$15;  { Receive a block of data from a node }

type
  buffer=array[1..buffsize] of byte;   { Buffer type declaration }
  buffp=^buffer;                       { Pointer type to the buffer }
  arrname=array[1..16] of char;        { Array for names type }

  { Message control block record }
  mcb=record
    mcb_command: byte;       { Command to execute }
    mcb_retcode: byte;       { Return code value }
    mcb_lsn: byte;           { Local session # }
    mcb_num: byte;           { Number of name added }
    mcb_buffer: pointer;     { Data buffer address }
    mcb_length: word;        { Buffer length in bytes }
    mcb_callname: arrname;   { Name on remote node }
    mcb_name: arrname;       { Name of local node }
    mcb_rto: byte;           { Receive timeout (NOT USED) }
    mcb_sto: byte;           { Send timeout (NOT USED) }
    mcb_post: pointer;       { Post routine address (NOT USED) }
    mcb_lana_num: byte;      { Adapter card to use. 0 is first }
    mcb_cmd_cpl: byte;       { Command status if NOWAIT is used }
    mcb_reserve: array[1..14] of byte; { Other detailed info }
  end;

{ Memory declarations }
var
   b: buffp;                 { Data buffer block }
   m: mcb;                   { Message control block }
   r: registers;             { Registers used in INT $5C }

   localname, callname: arrname; { Local and remote name variables }

   netaddr: pointer;          { NETBIOS $5C Interrupt address }

   fi: file;                  { File handle for reading or writing }
   filename: string[64];      { Filename path string }

   mode: char;                { Sending or receiving }
   nodenum: word;             { Our card's node number, 1-255 }

   remotenode,
   localnode: string[3];      { Remotes and local node numbers }

   lsn: byte;                 { Tracks our session number }

   fsize, bytecount: longint; { File size and bytes sent/received }
   count: word;               { Number of bytes to send/receive }

   noerr: boolean;            { General use error flag }
   ans: char;                 { Readkey variable }
{-------------------------------------------------------------------}
procedure init_mcb(var m:mcb);
{ Initialize a message control block to blanks and nulls }
begin
  m.mcb_command:=0;
  m.mcb_retcode:=$ff;      { Must be set to $FF }
  m.mcb_lsn:=0;
  m.mcb_num:=0;
  m.mcb_buffer:=nil;
  m.mcb_length:=0;
  fillchar(m.mcb_callname,16,' ');
  fillchar(m.mcb_name,16,' ');
  m.mcb_rto:=0;
  m.mcb_sto:=0;
  m.mcb_post:=nil;
  m.mcb_lana_num:=lancard;
  m.mcb_cmd_cpl:=0;
  fillchar(m.mcb_reserve,14,0);
end;
{-------------------------------------------------------------------}
procedure net_reset(var m:mcb);
{ Reset the node card }
begin
  init_mcb(m);
  m.mcb_command:=msg_reset;
  netaddr:=ptr(memw[0:$5c*4], memw[0:$5c*4+2]);
  if netaddr<>nil then
  begin
    r.es:=seg(m);
    r.bx:=ofs(m);
    intr($5c,r);
  end;
end;
{-------------------------------------------------------------------}
procedure terminate;
{ Terminate XNET }
begin
  close(fi);           { Close open file }
  if ioresult<>0 then ;{ Clear the error flag just in case }
  net_reset(m);        { Reset the adapter. Deletes all activity }
  freemem(b,buffsize); { Free heap memory (Out of Habit) }
  halt;                { Go have coffee and think about enhancements}
end;
{-------------------------------------------------------------------}
procedure net_error(var m: mcb);
{ Print a NETBIOS error and prompt user }
var ans: char;
  function hex(h:byte):string;
  { Convert a byte to hex notation }
  var i:byte;
      hexc:string[2];
  const
      hs:string[16]='0123456789ABCDEF';
  begin
    i:=(h shr 4);
    hexc:=hs[i+1];
    i:=(h and $0f);
    hexc:=hexc+hs[i+1];
    hex:=hexc;
  end;
begin
  if m.mcb_retcode=0 then exit;
  writeln('NETBIOS error code $',hex(m.mcb_retcode),
          ' in command code $',hex(m.mcb_command));
  ans:=readkey;
  terminate;
end;
{-------------------------------------------------------------------}
procedure net_status(var m:mcb; waitbit:byte; mcb_buffer:buffp;
                     mcb_length:word; mcb_callname:arrname;
                     mcb_post: pointer);
{ Get the current NETBIOS status }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_status;
  m.mcb_buffer:=mcb_buffer;
  m.mcb_length:=mcb_length;
  m.mcb_post:=mcb_post;
  move(mcb_callname,m.mcb_callname,16);
  netaddr:=ptr(memw[0:$5c*4], memw[0:$5c*4+2]);
  if netaddr<>nil then
  begin
    r.es:=seg(m);
    r.bx:=ofs(m);
    intr($5c,r);
  end;
end;
{-------------------------------------------------------------------}
procedure net_receive(var m:mcb; waitbit:byte; mcb_buffer:buffp;
                      mcb_length:word; mcb_lsn:byte;
                      mcb_post: pointer);
{ Wait to receive a data block from the node we are in session with }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_receive;
  m.mcb_buffer:=mcb_buffer;
  m.mcb_length:=mcb_length;
  m.mcb_lsn:=mcb_lsn;
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure net_hang_up(var m:mcb; waitbit:byte; mcb_lsn:byte;
                      mcb_post: pointer);
{ Hang up on the other guy. Not polite but who's perfect. }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_hang_up;
  m.mcb_lsn:=mcb_lsn;
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure net_send(var m:mcb; waitbit:byte; mcb_buffer:buffp;
                   mcb_length:word; mcb_lsn:byte; mcb_post: pointer);
{ Send a block of data to the node we are in session with. }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_send;
  m.mcb_buffer:=mcb_buffer;
  m.mcb_length:=mcb_length;
  m.mcb_lsn:=mcb_lsn;
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure net_add_name(var m:mcb; waitbit:byte; mcb_name:arrname;
                       mcb_post: pointer);
{ Tell NETBIOS our name. Must be unique anywhere in the network }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_add_name;
  move(mcb_name,m.mcb_name,16);
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure net_call(var m:mcb; waitbit:byte; mcb_callname,
                   mcb_name:arrname; mcb_post: pointer);
{ Call callname, and let him know we are ready }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_call;
  move(mcb_name,m.mcb_name,16);
  move(mcb_callname,m.mcb_callname,16);
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure net_listen(var m:mcb; waitbit:byte; mcb_callname,
                     mcb_name:arrname; mcb_post: pointer);
{ Listen if callname is calling us }
begin
  init_mcb(m);
  m.mcb_command:=waitbit+msg_listen;
  move(mcb_name,m.mcb_name,16);
  move(mcb_callname,m.mcb_callname,16);
  m.mcb_post:=mcb_post;
  r.es:=seg(m);
  r.bx:=ofs(m);
  intr($5c,r);
end;
{-------------------------------------------------------------------}
procedure copytoarr(s: string; var name: arrname);
{ Copy a string to a 16 byte array. Blank fill to end. }
begin
  fillchar(name,16,' ');
  move(s[1], name, length(s));
end;
{-------------------------------------------------------------------}
procedure send_the_file;
{
  Start sending file. First send the file size (2 words).
  Then send the rest in block of 64K with the remainder
  as the last block.
}
begin
  { Get file size and display }
  fsize:=filesize(fi);
  gotoxy(1,23); write('File size ',fsize);

  { Send the length of the file. Must be in 2 words }
  move(fsize, b^, 4);
  net_send(m, wait, b, 4, lsn, nil);
  net_error(m);

  bytecount:=0;
  noerr:=true;

  { Loop until the file is sent. }
  while (bytecount<fsize) and (noerr) do
  begin
    { Read a block and if no error then send }
    blockread(fi, b^, buffsize, count);
    if ioresult<>0 then
      noerr:=false
    else
    begin
      net_send(m, wait, b, count, lsn, nil);
      net_error(m);
      bytecount:=bytecount+count;
      gotoxy(1,24); write('File size sent ',bytecount,'      ');
    end;
  end;
end;
{-------------------------------------------------------------------}
procedure receive_the_file;
{
  Start receiving file and save to disk. First get the file size.
  Then receive in blocks of 64K with the remainder as the last block
}
begin
  { Get the file size. Block sent must be in 2 words }
  net_receive(m, wait, b, buffsize, lsn, nil);
  move(b^,fsize,4);
  { Display it }
  gotoxy(1,23); write('File size ',fsize);
  bytecount:=0;    { File size sent counter }
  noerr:=true;
  { Loop, receiving block in 64K increments }
  while (bytecount<fsize) and (noerr) do
  begin
    { Receive }
    net_receive(m, wait, b, buffsize, lsn, nil);
    net_error(m);
    { Save to file }
    blockwrite(fi, b^, m.mcb_length);
    { If an error abort else show file size sent so far. }
    if ioresult<>0 then
    begin
      noerr:=false;
      writeln('Disk full error');
      net_hang_up(m, wait, lsn, nil);
      terminate;
    end else
    begin
      bytecount:=bytecount+m.mcb_length;
      gotoxy(1,24); write('File size received ',bytecount,'      ');
    end;
  end;
end;
{-------------------------------------------------------------------}
procedure setup_call_send;
{ Ask for file name to send and call the remote station. Hopefully
  the remote is listening }
begin
  noerr:=true;
  { Get the file name to send }
  while noerr do
  begin
    write('Pathname of file to send (blank to exit)? ');
    readln(filename);
    if filename='' then terminate;
    assign(fi,filename);
    reset(fi,1);
    if ioresult<>0 then
      writeln('File does not exist.')
    else
      noerr:=false;
  end;
  { Get the local node and the remote node into arrays}
  copytoarr(localnode,localname);
  copytoarr(remotenode,callname);
  { Call 'callname' using our 'localname'. He should be
    expecting our call }
  noerr:=false;
  while not noerr do
  begin
    net_call(m, wait, callname, localname, nil);
    { Was the remote node available to listen? }
    if m.mcb_retcode<>0 then
    begin
      writeln('Remote Node, ',remotenode,' not ready. Retry/Abort?');
      ans:=readkey;
      if upcase(ans)='A' then net_error(m);
    end else
      noerr:=true;
  end;
  lsn:=m.mcb_lsn;  { Save the session number NETBIOS blessed us with}

  send_the_file;
  close(fi);
end;
{-------------------------------------------------------------------}
procedure setup_listen_receive;
{ Ask for file name to receive into and listen for the remote
  node's call }
begin
  noerr := true;
  { Get filename to save in to. If file exists verify and overwrite. }
  while noerr do
  begin
    write('Pathname of where to save received file (blank to exit)? ');
    readln(filename);
    if filename='' then terminate;
    assign(fi,filename);
    reset(fi);
    if ioresult=0 then
    begin
      writeln('File EXISTS. Do you wish to overwrite (Y/N)? ');
      ans:=readkey;
      if upcase(ans)='Y' then noerr:=false;
      close(fi);
    end else
      noerr:=false;
  end;
  rewrite(fi,1);
  { Get the local and remote nodes into array strings }
  copytoarr(localnode,localname);
  copytoarr(remotenode,callname);
  { Listen for the remote node to call up any moment }
  net_listen(m, wait, callname, localname, nil);
  lsn:=m.mcb_lsn;  { Save the session number NETBIOS blessed us with}
  net_error(m);

  receive_the_file;
  close(fi);
end;
{-------------------------------------------------------------------}
{ XNET Main program start                                           }
{-------------------------------------------------------------------}
begin

  clrscr;
  { Get a data buffer from the heap }
  getmem(b, buffsize);
  { Initialize fi to something }
  assign(fi,'NUL');
  { Are we supposed to reset? }
  net_reset(m);
  net_error(m);
  { Check our status }
  copytoarr('*', localname);  { Create our localname }
  { Check our node's NETBIOS status and
    in to the first get the node number (address) }
  net_status(m, wait, b, buffsize, localname, nil);
  net_error(m);
  { Get our node number and add it as a node name
    The node number is set by "net_status" and is
    in the first byte of the data buffer "b^"}
  nodenum:=mem[seg(b^):ofs(b^)];
  writeln('Your Station Number is: ',nodenum); writeln;

  str(nodenum,localnode);          { Convert to string array }
  copytoarr(localnode,localname);
  net_add_name(m, wait, localname, nil);  { Add to NETBIOS }
  net_error(m);
  { At this point the NETBIOS is aware of our presence }
  { Ask the user for the remote's node number.
    This is the node we wish to communicate with.
    It may not have the same number as our node }
  remotenode:=localnode;
  while (remotenode=localnode) do
  begin
    write('Enter remotes station #: ');
    readln(remotenode);
  end;
  { Ask for user's intentions. Send/Receive/Exit }
  writeln('[S]end-file, [R]eceive-file or [E]xit');
  mode:=readkey;

  case upcase(mode) of
    'S': setup_call_send;       { Send the file. }
    'R': setup_listen_receive;  { Receive the file. }
  end;
  terminate;
end.











Copyright © 1989, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.