home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj8910.zip / MENICO.LST < prev    next >
File List  |  1989-09-07  |  15KB  |  467 lines

  1. _High-Speed File Transfers With NetBios_
  2. by Costas Menico
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. program xnet;
  8. {
  9.   Program to demonstrate file transfer between PCs
  10.   using the NETBIOS device driver.  This program should work with
  11.   any hardware and software that support the NETBIOS interface.
  12.   Network software other than the NETBIOS is not required.
  13.   Program was tested with the PC260 Arcnet boards from SMC. The
  14.   CONFIG.SYS had the following line to install the NETBIOS:
  15.      device=smcarc.sys /p2e0 /i2 /me000
  16.   Program author: Costas Menico
  17. }
  18.  
  19. {$I-,R-}
  20. uses dos, crt;
  21.  
  22. const
  23.   { Maximum # of bytes to transfer in a single send }
  24.   buffsize = 64*1024-1;
  25.   lancard=0;    { Default network card }
  26.   nowait = $80; { Return immediately from command.
  27.                   Call the POST routine when done. (NOT USED)}
  28.   wait=$0;      { Wait until command is done. }
  29.  
  30.   { NETBIOS Commands used in this program }
  31.   msg_reset=$32;    { Reset the node }
  32.   msg_status=$33;   { Determine the current state of the node }
  33.   msg_add_name=$30; { Add a 16 char unique node name to NETBIOS }
  34.   msg_listen=$11;   { Listen for a node to establish session }
  35.   msg_call=$10;     { Call another node to establish a session }
  36.   msg_hang_up=$12;  { Hangup the session with a node }
  37.   msg_send=$14;     { Send a block of data to a node }
  38.   msg_receive=$15;  { Receive a block of data from a node }
  39.  
  40. type
  41.   buffer=array[1..buffsize] of byte;   { Buffer type declaration }
  42.   buffp=^buffer;                       { Pointer type to the buffer }
  43.   arrname=array[1..16] of char;        { Array for names type }
  44.  
  45.   { Message control block record }
  46.   mcb=record
  47.     mcb_command: byte;       { Command to execute }
  48.     mcb_retcode: byte;       { Return code value }
  49.     mcb_lsn: byte;           { Local session # }
  50.     mcb_num: byte;           { Number of name added }
  51.     mcb_buffer: pointer;     { Data buffer address }
  52.     mcb_length: word;        { Buffer length in bytes }
  53.     mcb_callname: arrname;   { Name on remote node }
  54.     mcb_name: arrname;       { Name of local node }
  55.     mcb_rto: byte;           { Receive timeout (NOT USED) }
  56.     mcb_sto: byte;           { Send timeout (NOT USED) }
  57.     mcb_post: pointer;       { Post routine address (NOT USED) }
  58.     mcb_lana_num: byte;      { Adapter card to use. 0 is first }
  59.     mcb_cmd_cpl: byte;       { Command status if NOWAIT is used }
  60.     mcb_reserve: array[1..14] of byte; { Other detailed info }
  61.   end;
  62.  
  63. { Memory declarations }
  64. var
  65.    b: buffp;                 { Data buffer block }
  66.    m: mcb;                   { Message control block }
  67.    r: registers;             { Registers used in INT $5C }
  68.  
  69.    localname, callname: arrname; { Local and remote name variables }
  70.  
  71.    netaddr: pointer;          { NETBIOS $5C Interrupt address }
  72.  
  73.    fi: file;                  { File handle for reading or writing }
  74.    filename: string[64];      { Filename path string }
  75.  
  76.    mode: char;                { Sending or receiving }
  77.    nodenum: word;             { Our card's node number, 1-255 }
  78.  
  79.    remotenode,
  80.    localnode: string[3];      { Remotes and local node numbers }
  81.  
  82.    lsn: byte;                 { Tracks our session number }
  83.  
  84.    fsize, bytecount: longint; { File size and bytes sent/received }
  85.    count: word;               { Number of bytes to send/receive }
  86.  
  87.    noerr: boolean;            { General use error flag }
  88.    ans: char;                 { Readkey variable }
  89. {-------------------------------------------------------------------}
  90. procedure init_mcb(var m:mcb);
  91. { Initialize a message control block to blanks and nulls }
  92. begin
  93.   m.mcb_command:=0;
  94.   m.mcb_retcode:=$ff;      { Must be set to $FF }
  95.   m.mcb_lsn:=0;
  96.   m.mcb_num:=0;
  97.   m.mcb_buffer:=nil;
  98.   m.mcb_length:=0;
  99.   fillchar(m.mcb_callname,16,' ');
  100.   fillchar(m.mcb_name,16,' ');
  101.   m.mcb_rto:=0;
  102.   m.mcb_sto:=0;
  103.   m.mcb_post:=nil;
  104.   m.mcb_lana_num:=lancard;
  105.   m.mcb_cmd_cpl:=0;
  106.   fillchar(m.mcb_reserve,14,0);
  107. end;
  108. {-------------------------------------------------------------------}
  109. procedure net_reset(var m:mcb);
  110. { Reset the node card }
  111. begin
  112.   init_mcb(m);
  113.   m.mcb_command:=msg_reset;
  114.   netaddr:=ptr(memw[0:$5c*4], memw[0:$5c*4+2]);
  115.   if netaddr<>nil then
  116.   begin
  117.     r.es:=seg(m);
  118.     r.bx:=ofs(m);
  119.     intr($5c,r);
  120.   end;
  121. end;
  122. {-------------------------------------------------------------------}
  123. procedure terminate;
  124. { Terminate XNET }
  125. begin
  126.   close(fi);           { Close open file }
  127.   if ioresult<>0 then ;{ Clear the error flag just in case }
  128.   net_reset(m);        { Reset the adapter. Deletes all activity }
  129.   freemem(b,buffsize); { Free heap memory (Out of Habit) }
  130.   halt;                { Go have coffee and think about enhancements}
  131. end;
  132. {-------------------------------------------------------------------}
  133. procedure net_error(var m: mcb);
  134. { Print a NETBIOS error and prompt user }
  135. var ans: char;
  136.   function hex(h:byte):string;
  137.   { Convert a byte to hex notation }
  138.   var i:byte;
  139.       hexc:string[2];
  140.   const
  141.       hs:string[16]='0123456789ABCDEF';
  142.   begin
  143.     i:=(h shr 4);
  144.     hexc:=hs[i+1];
  145.     i:=(h and $0f);
  146.     hexc:=hexc+hs[i+1];
  147.     hex:=hexc;
  148.   end;
  149. begin
  150.   if m.mcb_retcode=0 then exit;
  151.   writeln('NETBIOS error code $',hex(m.mcb_retcode),
  152.           ' in command code $',hex(m.mcb_command));
  153.   ans:=readkey;
  154.   terminate;
  155. end;
  156. {-------------------------------------------------------------------}
  157. procedure net_status(var m:mcb; waitbit:byte; mcb_buffer:buffp;
  158.                      mcb_length:word; mcb_callname:arrname;
  159.                      mcb_post: pointer);
  160. { Get the current NETBIOS status }
  161. begin
  162.   init_mcb(m);
  163.   m.mcb_command:=waitbit+msg_status;
  164.   m.mcb_buffer:=mcb_buffer;
  165.   m.mcb_length:=mcb_length;
  166.   m.mcb_post:=mcb_post;
  167.   move(mcb_callname,m.mcb_callname,16);
  168.   netaddr:=ptr(memw[0:$5c*4], memw[0:$5c*4+2]);
  169.   if netaddr<>nil then
  170.   begin
  171.     r.es:=seg(m);
  172.     r.bx:=ofs(m);
  173.     intr($5c,r);
  174.   end;
  175. end;
  176. {-------------------------------------------------------------------}
  177. procedure net_receive(var m:mcb; waitbit:byte; mcb_buffer:buffp;
  178.                       mcb_length:word; mcb_lsn:byte;
  179.                       mcb_post: pointer);
  180. { Wait to receive a data block from the node we are in session with }
  181. begin
  182.   init_mcb(m);
  183.   m.mcb_command:=waitbit+msg_receive;
  184.   m.mcb_buffer:=mcb_buffer;
  185.   m.mcb_length:=mcb_length;
  186.   m.mcb_lsn:=mcb_lsn;
  187.   m.mcb_post:=mcb_post;
  188.   r.es:=seg(m);
  189.   r.bx:=ofs(m);
  190.   intr($5c,r);
  191. end;
  192. {-------------------------------------------------------------------}
  193. procedure net_hang_up(var m:mcb; waitbit:byte; mcb_lsn:byte;
  194.                       mcb_post: pointer);
  195. { Hang up on the other guy. Not polite but who's perfect. }
  196. begin
  197.   init_mcb(m);
  198.   m.mcb_command:=waitbit+msg_hang_up;
  199.   m.mcb_lsn:=mcb_lsn;
  200.   m.mcb_post:=mcb_post;
  201.   r.es:=seg(m);
  202.   r.bx:=ofs(m);
  203.   intr($5c,r);
  204. end;
  205. {-------------------------------------------------------------------}
  206. procedure net_send(var m:mcb; waitbit:byte; mcb_buffer:buffp;
  207.                    mcb_length:word; mcb_lsn:byte; mcb_post: pointer);
  208. { Send a block of data to the node we are in session with. }
  209. begin
  210.   init_mcb(m);
  211.   m.mcb_command:=waitbit+msg_send;
  212.   m.mcb_buffer:=mcb_buffer;
  213.   m.mcb_length:=mcb_length;
  214.   m.mcb_lsn:=mcb_lsn;
  215.   m.mcb_post:=mcb_post;
  216.   r.es:=seg(m);
  217.   r.bx:=ofs(m);
  218.   intr($5c,r);
  219. end;
  220. {-------------------------------------------------------------------}
  221. procedure net_add_name(var m:mcb; waitbit:byte; mcb_name:arrname;
  222.                        mcb_post: pointer);
  223. { Tell NETBIOS our name. Must be unique anywhere in the network }
  224. begin
  225.   init_mcb(m);
  226.   m.mcb_command:=waitbit+msg_add_name;
  227.   move(mcb_name,m.mcb_name,16);
  228.   m.mcb_post:=mcb_post;
  229.   r.es:=seg(m);
  230.   r.bx:=ofs(m);
  231.   intr($5c,r);
  232. end;
  233. {-------------------------------------------------------------------}
  234. procedure net_call(var m:mcb; waitbit:byte; mcb_callname,
  235.                    mcb_name:arrname; mcb_post: pointer);
  236. { Call callname, and let him know we are ready }
  237. begin
  238.   init_mcb(m);
  239.   m.mcb_command:=waitbit+msg_call;
  240.   move(mcb_name,m.mcb_name,16);
  241.   move(mcb_callname,m.mcb_callname,16);
  242.   m.mcb_post:=mcb_post;
  243.   r.es:=seg(m);
  244.   r.bx:=ofs(m);
  245.   intr($5c,r);
  246. end;
  247. {-------------------------------------------------------------------}
  248. procedure net_listen(var m:mcb; waitbit:byte; mcb_callname,
  249.                      mcb_name:arrname; mcb_post: pointer);
  250. { Listen if callname is calling us }
  251. begin
  252.   init_mcb(m);
  253.   m.mcb_command:=waitbit+msg_listen;
  254.   move(mcb_name,m.mcb_name,16);
  255.   move(mcb_callname,m.mcb_callname,16);
  256.   m.mcb_post:=mcb_post;
  257.   r.es:=seg(m);
  258.   r.bx:=ofs(m);
  259.   intr($5c,r);
  260. end;
  261. {-------------------------------------------------------------------}
  262. procedure copytoarr(s: string; var name: arrname);
  263. { Copy a string to a 16 byte array. Blank fill to end. }
  264. begin
  265.   fillchar(name,16,' ');
  266.   move(s[1], name, length(s));
  267. end;
  268. {-------------------------------------------------------------------}
  269. procedure send_the_file;
  270. {
  271.   Start sending file. First send the file size (2 words).
  272.   Then send the rest in block of 64K with the remainder
  273.   as the last block.
  274. }
  275. begin
  276.   { Get file size and display }
  277.   fsize:=filesize(fi);
  278.   gotoxy(1,23); write('File size ',fsize);
  279.  
  280.   { Send the length of the file. Must be in 2 words }
  281.   move(fsize, b^, 4);
  282.   net_send(m, wait, b, 4, lsn, nil);
  283.   net_error(m);
  284.  
  285.   bytecount:=0;
  286.   noerr:=true;
  287.  
  288.   { Loop until the file is sent. }
  289.   while (bytecount<fsize) and (noerr) do
  290.   begin
  291.     { Read a block and if no error then send }
  292.     blockread(fi, b^, buffsize, count);
  293.     if ioresult<>0 then
  294.       noerr:=false
  295.     else
  296.     begin
  297.       net_send(m, wait, b, count, lsn, nil);
  298.       net_error(m);
  299.       bytecount:=bytecount+count;
  300.       gotoxy(1,24); write('File size sent ',bytecount,'      ');
  301.     end;
  302.   end;
  303. end;
  304. {-------------------------------------------------------------------}
  305. procedure receive_the_file;
  306. {
  307.   Start receiving file and save to disk. First get the file size.
  308.   Then receive in blocks of 64K with the remainder as the last block
  309. }
  310. begin
  311.   { Get the file size. Block sent must be in 2 words }
  312.   net_receive(m, wait, b, buffsize, lsn, nil);
  313.   move(b^,fsize,4);
  314.   { Display it }
  315.   gotoxy(1,23); write('File size ',fsize);
  316.   bytecount:=0;    { File size sent counter }
  317.   noerr:=true;
  318.   { Loop, receiving block in 64K increments }
  319.   while (bytecount<fsize) and (noerr) do
  320.   begin
  321.     { Receive }
  322.     net_receive(m, wait, b, buffsize, lsn, nil);
  323.     net_error(m);
  324.     { Save to file }
  325.     blockwrite(fi, b^, m.mcb_length);
  326.     { If an error abort else show file size sent so far. }
  327.     if ioresult<>0 then
  328.     begin
  329.       noerr:=false;
  330.       writeln('Disk full error');
  331.       net_hang_up(m, wait, lsn, nil);
  332.       terminate;
  333.     end else
  334.     begin
  335.       bytecount:=bytecount+m.mcb_length;
  336.       gotoxy(1,24); write('File size received ',bytecount,'      ');
  337.     end;
  338.   end;
  339. end;
  340. {-------------------------------------------------------------------}
  341. procedure setup_call_send;
  342. { Ask for file name to send and call the remote station. Hopefully
  343.   the remote is listening }
  344. begin
  345.   noerr:=true;
  346.   { Get the file name to send }
  347.   while noerr do
  348.   begin
  349.     write('Pathname of file to send (blank to exit)? ');
  350.     readln(filename);
  351.     if filename='' then terminate;
  352.     assign(fi,filename);
  353.     reset(fi,1);
  354.     if ioresult<>0 then
  355.       writeln('File does not exist.')
  356.     else
  357.       noerr:=false;
  358.   end;
  359.   { Get the local node and the remote node into arrays}
  360.   copytoarr(localnode,localname);
  361.   copytoarr(remotenode,callname);
  362.   { Call 'callname' using our 'localname'. He should be
  363.     expecting our call }
  364.   noerr:=false;
  365.   while not noerr do
  366.   begin
  367.     net_call(m, wait, callname, localname, nil);
  368.     { Was the remote node available to listen? }
  369.     if m.mcb_retcode<>0 then
  370.     begin
  371.       writeln('Remote Node, ',remotenode,' not ready. Retry/Abort?');
  372.       ans:=readkey;
  373.       if upcase(ans)='A' then net_error(m);
  374.     end else
  375.       noerr:=true;
  376.   end;
  377.   lsn:=m.mcb_lsn;  { Save the session number NETBIOS blessed us with}
  378.  
  379.   send_the_file;
  380.   close(fi);
  381. end;
  382. {-------------------------------------------------------------------}
  383. procedure setup_listen_receive;
  384. { Ask for file name to receive into and listen for the remote
  385.   node's call }
  386. begin
  387.   noerr := true;
  388.   { Get filename to save in to. If file exists verify and overwrite. }
  389.   while noerr do
  390.   begin
  391.     write('Pathname of where to save received file (blank to exit)? ');
  392.     readln(filename);
  393.     if filename='' then terminate;
  394.     assign(fi,filename);
  395.     reset(fi);
  396.     if ioresult=0 then
  397.     begin
  398.       writeln('File EXISTS. Do you wish to overwrite (Y/N)? ');
  399.       ans:=readkey;
  400.       if upcase(ans)='Y' then noerr:=false;
  401.       close(fi);
  402.     end else
  403.       noerr:=false;
  404.   end;
  405.   rewrite(fi,1);
  406.   { Get the local and remote nodes into array strings }
  407.   copytoarr(localnode,localname);
  408.   copytoarr(remotenode,callname);
  409.   { Listen for the remote node to call up any moment }
  410.   net_listen(m, wait, callname, localname, nil);
  411.   lsn:=m.mcb_lsn;  { Save the session number NETBIOS blessed us with}
  412.   net_error(m);
  413.  
  414.   receive_the_file;
  415.   close(fi);
  416. end;
  417. {-------------------------------------------------------------------}
  418. { XNET Main program start                                           }
  419. {-------------------------------------------------------------------}
  420. begin
  421.  
  422.   clrscr;
  423.   { Get a data buffer from the heap }
  424.   getmem(b, buffsize);
  425.   { Initialize fi to something }
  426.   assign(fi,'NUL');   
  427.   { Are we supposed to reset? }
  428.   net_reset(m);
  429.   net_error(m);
  430.   { Check our status }
  431.   copytoarr('*', localname);  { Create our localname }
  432.   { Check our node's NETBIOS status and
  433.     in to the first get the node number (address) }
  434.   net_status(m, wait, b, buffsize, localname, nil);
  435.   net_error(m);
  436.   { Get our node number and add it as a node name
  437.     The node number is set by "net_status" and is
  438.     in the first byte of the data buffer "b^"}
  439.   nodenum:=mem[seg(b^):ofs(b^)];
  440.   writeln('Your Station Number is: ',nodenum); writeln;
  441.  
  442.   str(nodenum,localnode);          { Convert to string array }
  443.   copytoarr(localnode,localname);
  444.   net_add_name(m, wait, localname, nil);  { Add to NETBIOS }
  445.   net_error(m);
  446.   { At this point the NETBIOS is aware of our presence }
  447.   { Ask the user for the remote's node number.
  448.     This is the node we wish to communicate with.
  449.     It may not have the same number as our node }
  450.   remotenode:=localnode;
  451.   while (remotenode=localnode) do
  452.   begin
  453.     write('Enter remotes station #: ');
  454.     readln(remotenode);
  455.   end;
  456.   { Ask for user's intentions. Send/Receive/Exit }
  457.   writeln('[S]end-file, [R]eceive-file or [E]xit');
  458.   mode:=readkey;
  459.  
  460.   case upcase(mode) of
  461.     'S': setup_call_send;       { Send the file. }
  462.     'R': setup_listen_receive;  { Receive the file. }
  463.   end;
  464.   terminate;
  465. end.
  466.  
  467.