home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / TCPConnections.p < prev    next >
Encoding:
Text File  |  1996-05-29  |  16.6 KB  |  634 lines  |  [TEXT/CWIE]

  1. unit TCPConnections;
  2.  
  3. { TCPConnections © Peter Lewis, Oct 1991 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         TCPStuff;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.  
  13.     const
  14.         any_connection = 0;    { Pass to GetConnectionEvent }
  15.         no_connection = -1;    { Guaranteed invalid connection }
  16.  
  17.     type
  18.         connectionIndex = longint;
  19.         connectionEvent = (C_NoEvent, C_Found, C_SearchFailed, C_NameFound, C_NameSearchFailed,{}
  20.             C_Established, C_FailedToOpen, C_Closing, C_Closed, C_CharsAvailable, C_HeartBeat);
  21.         connectionEventRecord = record
  22.                 event: connectionEvent;
  23.                 connection: connectionIndex;
  24.                 tcpc: TCPConnectionPtr;
  25.                 dataptr: Ptr;
  26.                 value: longint;
  27.                 timedout: boolean;
  28.             end;
  29.  
  30.     function InitConnections: OSErr;
  31.     procedure CloseConnections;
  32.     procedure TerminateConnections;
  33.     function CanQuit: boolean;
  34. { After Terminate, keep calling GetConnectionEvent(any_connection,cer) until CanQuit is true, then Finish }
  35.     procedure FinishConnections;
  36.     procedure FinishEverything;  { Or just call FinishEverything }
  37.     function FindAddress (var cp: connectionIndex; hostName: Str255; dataptr: univ Ptr): OSErr;
  38.     function FindName (var cp: connectionIndex; hostIP: longint; dataptr: univ Ptr): OSErr;
  39.     procedure FindString (hostIP: longint; var s: Str255);
  40.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longint; localport: integer; remotehost: longint; remoteport: integer; dataptr: univ Ptr): OSErr;
  41.     function NewActiveConnection (var cp: connectionIndex; buffersize: longint; localport: integer; remotehost: longint; remoteport: integer; dataptr: univ Ptr): OSErr;
  42.     procedure CloseTCPConnection (cp: connectionIndex); { CloseConnection is defined in InterfaceLIB }
  43.     procedure AbortConnection (cp: connectionIndex); { Violently close connection }
  44.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  45. { Pass any_connection for any event, otherwise cp specifies the event }
  46.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ Ptr);
  47.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ Ptr);
  48.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longint);
  49.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longint);
  50.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  51.     procedure SetHeartBeat (cp: connectionIndex; n: longint); { Send C_HeartBeat every n ticks, 0 disables Heartbeat }
  52.  
  53. implementation
  54.  
  55.     uses
  56.         Textutils, DNR, Errors, MyMemory;
  57.  
  58.     const
  59.         TCPCMagic = 'TCPC';
  60.         TCPCBadMagic = 'badc';
  61.  
  62.     const  { Tuning parameters }
  63.         max_connections = 40;
  64.         TO_FindAddress = 40 * 60;
  65.         TO_FindName = 40 * 60;
  66.         TO_ActiveOpen = 20 * 60;
  67.         TO_Closing = longint(2) * 60 * 60;
  68.         TO_PassiveOpen = longint(1) * 365 * 24 * 3600 * 60;  { Ten years should be safe enough right? :-) }
  69.  
  70.     type
  71.         myHostInfo = record
  72.                 hi: hostInfo;
  73.                 done: SignedByte;
  74.             end;
  75.         myHostInfoPtr = ^myHostInfo;
  76.         statusType = (CS_None, CS_Searching, CS_NameSearching, CS_Opening, CS_Established, CS_Closing);
  77.         connectionRecord = record
  78.                 magic: OSType;
  79.                 conmagic: longint;
  80.                 tcpc: TCPConnectionPtr;
  81.                 status: statusType;
  82.                 dnrrp: DNRRecordPtr;
  83.                 closedone: boolean;
  84.                 timeout: longint;
  85.                 dataptr: Ptr;
  86.                 Heartbeat: longint; { Time for next Heartbeat }
  87.                 period: longint; { Ticks per Heartbeat }
  88.             end;
  89.  
  90.     var
  91.         connections: array[1..max_connections] of connectionRecord;
  92.         connectionItem: connectionIndex;
  93.         connectionmagic: longint;
  94.  
  95.     function ValidConnectionSafe (var cp: connectionIndex): boolean;
  96.         var
  97.             ocp: longint;
  98.             vc: boolean;
  99.     begin
  100.         vc := false;
  101.         ocp := cp;
  102.         cp := cp mod (max_connections + 1);
  103.         if cp > 0 then begin
  104.             if connections[cp].magic = TCPCMagic then begin
  105.                 if connections[cp].conmagic = ocp then begin
  106.                     vc := true;
  107.                 end;
  108.             end;
  109.         end;
  110.         ValidConnectionSafe := vc;
  111.     end;
  112.  
  113.     function ValidConnection (var cp: connectionIndex): boolean;
  114.         var
  115.             vc: boolean;
  116.     begin
  117.         vc := ValidConnectionSafe(cp);
  118.         if not vc then begin
  119.             DebugStr('Invalid Connection');
  120.         end;
  121.         ValidConnection := vc;
  122.     end;
  123.  
  124.     procedure SetDataPtr (cp: connectionIndex; dataptr: univ Ptr);
  125.     begin
  126.         if ValidConnection(cp) then begin
  127.             connections[cp].dataptr := dataptr;
  128.         end;
  129.     end;
  130.  
  131.     procedure GetDataPtr (cp: connectionIndex; var dataptr: univ Ptr);
  132.     begin
  133.         if ValidConnectionSafe(cp) then begin
  134.             dataptr := connections[cp].dataptr;
  135.         end else begin
  136.             dataptr := nil;
  137.         end;
  138.     end;
  139.  
  140.     procedure SetConnectionTimeout (cp: connectionIndex; timeout: longint);
  141.     begin
  142.         if ValidConnection(cp) then begin
  143.             connections[cp].timeout := timeout;
  144.         end;
  145.     end;
  146.  
  147.     procedure GetConnectionTimeout (cp: connectionIndex; var timeout: longint);
  148.     begin
  149.         if ValidConnection(cp) then begin
  150.             timeout := connections[cp].timeout;
  151.         end else begin
  152.             timeout := -1;
  153.         end;
  154.     end;
  155.  
  156.     procedure SetHeartBeat (cp: connectionIndex; n: longint); { Send C_HeartBeat every n ticks }
  157.     begin
  158.         if ValidConnection(cp) then begin
  159.             if (n < 1) or (n = maxLongInt) then begin
  160.                 connections[cp].period := maxLongInt;
  161.                 connections[cp].Heartbeat := maxLongInt;
  162.             end
  163.             else begin
  164.                 connections[cp].period := n;
  165.                 connections[cp].Heartbeat := TickCount + n;
  166.             end;
  167.         end;
  168.     end;
  169.  
  170.     procedure GetConnectionTCPC (cp: connectionIndex; var tcpc: TCPConnectionPtr);
  171.     begin
  172.         if ValidConnectionSafe(cp) then begin
  173.             tcpc := connections[cp].tcpc;
  174.         end else begin
  175.             tcpc := nil;
  176.         end;
  177.     end;
  178.  
  179.     function MyTCPState (con: TCPConnectionPtr): TCPStateType;
  180.     begin
  181.         if con = nil then begin
  182.             MyTCPState := T_Closed;
  183.         end else begin
  184.             MyTCPState := TCPState(con);
  185.         end;
  186.     end;
  187.  
  188.     function InitConnections: OSErr;
  189.         var
  190.             oe: OSErr;
  191.             i: connectionIndex;
  192.     begin
  193.         for i := 1 to max_connections do begin
  194.             connections[i].magic := TCPCBadMagic;
  195.         end;
  196.         connectionmagic := 0;
  197.         connectionItem := 1;
  198.         oe := TCPInit;
  199.         if oe = noErr then begin
  200.             oe := OpenResolver;
  201.             if oe <> noErr then begin
  202.                 TCPFinish;
  203.             end;
  204.         end;
  205.         InitConnections := oe;
  206.     end;
  207.  
  208.     procedure TerminateConnections;
  209.         var
  210.             i: connectionIndex;
  211.             oe: OSErr;
  212.     begin
  213.         for i := 1 to max_connections do begin
  214.             with connections[i] do begin
  215.                 if magic = TCPCMagic then begin
  216.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then begin
  217.                         if TCPState(tcpc) <> T_Closed then begin
  218.                             oe := TCPAbort(tcpc);
  219.                         end;
  220.                     end;
  221.                 end;
  222.             end;
  223.         end;
  224.     end;
  225.  
  226.     procedure CloseConnections;
  227.         var
  228.             i: connectionIndex;
  229.             oe: OSErr;
  230.     begin
  231.         for i := 1 to max_connections do begin
  232.             with connections[i] do begin
  233.                 if magic = TCPCMagic then begin
  234.                     if (status = CS_Established) or (status = CS_Opening) or (status = CS_Closing) then begin
  235.                         if TCPState(tcpc) <> T_Closed then begin
  236.                             oe := TCPClose(tcpc, nil);
  237.                         end;
  238.                     end;
  239.                 end;
  240.             end;
  241.         end;
  242.     end;
  243.  
  244.     function CanQuit: boolean;
  245.         var
  246.             i: connectionIndex;
  247.     begin
  248.         CanQuit := true;
  249.         for i := 1 to max_connections do begin
  250.             if connections[i].magic = TCPCMagic then begin
  251.                 CanQuit := false;
  252.             end;
  253.         end;
  254.     end;
  255.  
  256.     procedure FinishConnections;
  257.     begin
  258.         CloseResolver;
  259.         TCPFinish;
  260.     end;
  261.  
  262.     procedure FinishEverything;
  263.         var
  264.             cer: connectionEventRecord;
  265.             dummy: boolean;
  266.             er: eventrecord;
  267.     begin
  268.         TerminateConnections;
  269.         while not CanQuit do begin
  270.             if GetConnectionEvent(any_connection, cer) then begin
  271.                 dummy := WaitNextEvent(everyEvent, er, 0, nil);
  272.             end
  273.             else
  274.                 dummy := WaitNextEvent(everyEvent, er, 5, nil);
  275.         end;
  276.         FinishConnections;
  277.     end;
  278.  
  279.     function CreateConnection (var cp: connectionIndex; dp: Ptr): OSErr;
  280.     begin
  281.         connectionmagic := connectionmagic + max_connections + 1;
  282.         cp := 1;
  283.         while (connections[cp].magic = TCPCMagic) and (cp < max_connections) do begin
  284.             cp := cp + 1;
  285.         end;
  286.         with connections[cp] do begin
  287.             if magic = TCPCMagic then begin
  288.                 CreateConnection := tooManyConnections;
  289.             end else begin
  290.                 magic := TCPCMagic;
  291.                 conmagic := cp + connectionmagic;
  292.                 closedone := false;
  293.                 tcpc := nil;
  294.                 status := CS_None;
  295.                 dnrrp := nil;
  296.                 timeout := maxlongInt;
  297.                 dataptr := dp;
  298.                 period := maxLongInt;
  299.                 Heartbeat := maxLongInt;
  300.                 CreateConnection := noErr;
  301.                 cp := cp + connectionmagic;
  302.  
  303.             end;
  304.         end;
  305.     end;
  306.  
  307.     procedure DestroyConnection (var cp: connectionIndex);
  308.     begin
  309.         if ValidConnection(cp) then begin
  310.             connections[cp].magic := TCPCBadMagic;
  311.         end;
  312.         cp := -1;
  313.     end;
  314.  
  315.     function FindAddress (var cp: connectionIndex; hostName: Str255; dataptr: univ Ptr): OSErr;
  316.         var
  317.             oe: OSErr;
  318.             cpi: connectionIndex;
  319.     begin
  320.         oe := CreateConnection(cp, dataptr);
  321.         if oe = noErr then begin
  322.             cpi := cp;
  323.             if ValidConnection(cpi) then begin
  324.                 with connections[cpi] do begin
  325.                     oe := MNewPtr(dnrrp, SizeOf(DNRRecord));
  326.                     if oe = noErr then begin
  327.                         DNRNameToAddr(hostName, dnrrp, nil);
  328.                         timeout := TickCount + TO_FindAddress;
  329.                         status := CS_Searching;
  330.                     end;
  331.                 end;
  332.             end;
  333.             if oe <> noErr then begin
  334.                 DestroyConnection(cp);
  335.             end;
  336.         end;
  337.         FindAddress := oe;
  338.     end;
  339.  
  340.     procedure FindString (hostIP: longint; var s: Str255);
  341.     begin
  342.         AddrToStr(hostIP, s);
  343.     end;
  344.  
  345.     function FindName (var cp: connectionIndex; hostIP: longint; dataptr: univ Ptr): OSErr;
  346.         var
  347.             oe: OSErr;
  348.             cpi: connectionIndex;
  349.     begin
  350.         oe := CreateConnection(cp, dataptr);
  351.         if oe = noErr then begin
  352.             cpi := cp;
  353.             if ValidConnection(cpi) then begin
  354.                 with connections[cpi] do begin
  355.                     oe := MNewPtr(dnrrp, SizeOf(DNRRecord));
  356.                     if oe = noErr then begin
  357.                         DNRAddrToName(hostIP, dnrrp, nil);
  358.                         timeout := TickCount + TO_FindName;
  359.                         status := CS_NameSearching;
  360.                     end;
  361.                 end;
  362.             end;
  363.             if oe <> noErr then begin
  364.                 DestroyConnection(cp);
  365.             end;
  366.         end;
  367.         FindName := oe;
  368.     end;
  369.  
  370.     function NewPassiveConnection (var cp: connectionIndex; buffersize: longint; localport: integer; remotehost: longint; remoteport: integer; dataptr: univ Ptr): OSErr;
  371.         var
  372.             oe: OSErr;
  373.             cpi: connectionIndex;
  374.     begin
  375.         oe := CreateConnection(cp, dataptr);
  376.         if oe = noErr then begin
  377.             cpi := cp;
  378.             if ValidConnection(cpi) then begin
  379.                 with connections[cpi] do begin
  380.                     oe := TCPPassiveOpen(tcpc, buffersize, localport, remotehost, remoteport, nil);
  381.                     timeout := TickCount + TO_PassiveOpen;
  382.                     status := CS_Opening;
  383.                     if oe <> noErr then begin
  384.                         DestroyConnection(cp);
  385.                     end;
  386.                 end;
  387.             end;
  388.         end;
  389.         NewPassiveConnection := oe;
  390.     end;
  391.  
  392.     function NewActiveConnection (var cp: connectionIndex; buffersize: longint; localport: integer; remotehost: longint; remoteport: integer; dataptr: univ Ptr): OSErr;
  393.         var
  394.             oe: OSErr;
  395.             cpi: connectionIndex;
  396.     begin
  397.         oe := CreateConnection(cp, dataptr);
  398.         if oe = noErr then begin
  399.             cpi := cp;
  400.             if ValidConnection(cpi) then begin
  401.                 with connections[cpi] do begin
  402.                     oe := TCPActiveOpen(tcpc, buffersize, localport, remotehost, remoteport, nil);
  403.                     timeout := TickCount + TO_ActiveOpen;
  404.                     status := CS_Opening;
  405.                     if oe <> noErr then begin
  406.                         DestroyConnection(cp);
  407.                     end;
  408.                 end;
  409.             end;
  410.         end;
  411.         NewActiveConnection := oe;
  412.     end;
  413.  
  414.     procedure CloseTCPConnection (cp: connectionIndex);
  415.         var
  416.             err: OSErr;
  417.     begin
  418.         if ValidConnection(cp) then begin
  419.             with connections[cp] do begin
  420.                 if not closedone then begin
  421.                     if MyTCPState(tcpc) <> T_Closed then begin
  422.                         err := TCPClose(tcpc, nil);
  423.                     end;
  424.                     closedone := true;
  425.                 end;
  426.                 status := CS_Closing;
  427.                 timeout := TickCount + TO_Closing;
  428.             end;
  429.         end;
  430.     end;
  431.  
  432.     procedure AbortConnection (cp: connectionIndex);
  433.         var
  434.             err: OSErr;
  435.     begin
  436.         if ValidConnection(cp) then begin
  437.             with connections[cp] do begin
  438.                 if MyTCPState(tcpc) <> T_Closed then begin
  439.                     err := TCPAbort(tcpc);
  440.                 end;
  441.                 status := CS_Closing;
  442.                 timeout := TickCount + TO_Closing;
  443.             end;
  444.         end;
  445.     end;
  446.  
  447.     function GetConnectionEvent (cp: connectionIndex; var cer: connectionEventRecord): boolean;
  448.         procedure HandleConnection (cp: connectionIndex);
  449.             var
  450.                 oe: OSErr;
  451.                 rcp: connectionIndex;
  452.                 tstate : TCPStateType;
  453.         begin
  454.             with connections[cp] do begin
  455.                 rcp := conmagic;
  456.                 cer.connection := rcp;
  457.                 cer.tcpc := tcpc;
  458.                 cer.dataptr := dataptr;
  459.                 cer.timedout := false;
  460.                 case status of
  461.                     CS_NameSearching: 
  462.                         with dnrrp^ do begin
  463.                             if ioResult <> inProgress then begin
  464.                                 if ioResult = noErr then begin
  465.                                     cer.event := C_NameFound;
  466.                                     StringHandle(cer.value) := NewString(name);
  467.                                 end
  468.                                 else begin
  469.                                     cer.event := C_NameSearchFailed;
  470.                                     cer.value := ioResult;
  471.                                 end
  472.                             end
  473.                             else if TickCount > timeout then begin
  474.                                 cer.event := C_NameSearchFailed;
  475.                                 cer.value := 1;
  476.                                 cer.timedout := true;
  477.                             end;
  478.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  479.                                 if ioResult <> inProgress then begin  { If we timed out, then we'll just have to abandon this block.  Oh well }
  480.                                     MDisposePtr(dnrrp);
  481.                                 end;
  482.                                 dnrrp := nil;
  483.                                 DestroyConnection(rcp);
  484.                             end; {if}
  485.                         end; {with}
  486.                     CS_Searching: 
  487.                         with dnrrp^ do begin
  488.                             if ioResult = noErr then begin
  489.                                 cer.event := C_Found;
  490.                                 cer.value := addr;
  491.                             end
  492.                             else if ioResult <> inProgress then begin
  493.                                 cer.event := C_SearchFailed;
  494.                                 cer.value := ioResult;
  495.                             end
  496.                             else if TickCount > timeout then begin
  497.                                 cer.event := C_SearchFailed;
  498.                                 cer.value := 1;
  499.                                 cer.timedout := true;
  500.                             end;
  501.                             if cer.event <> C_NoEvent then begin  { Destroy the connection now }
  502.                                 if ioResult <> inProgress then begin  { If we timed out, then we'll just have to abandon this block.  Oh well }
  503.                                     MDisposePtr(dnrrp);
  504.                                 end;
  505.                                 dnrrp := nil;
  506.                                 DestroyConnection(rcp);
  507.                             end; {if}
  508.                         end; {with}
  509.                     CS_Opening:  begin
  510.                         case MyTCPState(tcpc) of
  511.                             T_WaitingForOpen, T_Opening, T_Listening: 
  512.                                 if TickCount > timeout then begin
  513.                                     CloseTCPConnection(rcp);
  514.                                     cer.event := C_FailedToOpen;
  515.                                     cer.timedout := true;
  516.                                 end;
  517.                             T_Established:  begin
  518.                                 cer.event := C_Established;
  519.                                 status := CS_Established;
  520.                                 timeout := maxLongInt;
  521.                             end;
  522.                             T_PleaseClose, T_Closing:  begin
  523.                                 CloseTCPConnection(rcp);
  524.                                 cer.value := 1;
  525.                                 cer.event := C_FailedToOpen;
  526.                                 timeout := TickCount + TO_Closing;
  527.                             end;
  528.                             T_Closed:  begin
  529.                                 status := CS_Closing;
  530.                                 cer.value := 2;
  531.                                 cer.event := C_FailedToOpen;
  532.                                 timeout := TickCount + TO_Closing;
  533.                             end;
  534.                             otherwise
  535.                                 ;
  536.                         end; {case }
  537.                     end;
  538.                     CS_Established:  begin
  539.                         case MyTCPState(tcpc) of
  540.                             T_Established:  begin
  541.                                 cer.value := TCPCharsAvailable(tcpc);
  542.                                 if cer.value > 0 then begin
  543.                                     cer.event := C_CharsAvailable;
  544.                                 end;
  545.                             end;
  546.                             T_PleaseClose, T_Closing:  begin
  547.                                 cer.value := TCPCharsAvailable(tcpc);
  548.                                 if cer.value > 0 then begin
  549.                                     cer.event := C_CharsAvailable;
  550.                                 end else begin
  551. {    CloseTCPConnection(rcp);}
  552.                                     status := CS_Closing;
  553.                                     cer.event := C_Closing;
  554.                                     timeout := TickCount + TO_Closing;
  555.                                 end;
  556.                             end;
  557.  
  558.                             T_Closed:  begin
  559.                                 status := CS_Closing;
  560.                                 cer.event := C_Closing;
  561.                                 timeout := TickCount + TO_Closing;
  562.                             end;
  563.                             otherwise
  564.                                 ;
  565.                         end;
  566.                     end;
  567.                     CS_Closing:  begin
  568.                         tstate := MyTCPState(tcpc);
  569.                         case tstate of
  570.                             T_WaitingForOpen, T_Opening, T_Listening: 
  571.                                 ;
  572. {DebugStr('Strange State 2')}
  573.                             T_PleaseClose, T_Closing, T_Established:  begin
  574.                                 cer.value := TCPCharsAvailable(tcpc);
  575.                                 if cer.value > 0 then begin
  576.                                     cer.event := C_CharsAvailable;
  577.                                 end else if TickCount > timeout then begin
  578.                                     cer.event := C_Closed;
  579.                                     if tcpc <> nil then begin
  580. {DebugStr('Closing timeout, call Abort;g');}
  581. {oe := TCPAbort(tcpc);}
  582.                                         oe := TCPRelease(tcpc);
  583.                                     end;
  584.                                     cer.timedout := true;
  585.                                     DestroyConnection(rcp);
  586.                                 end;
  587.                             end;
  588.                             T_Closed:  begin
  589.                                 cer.event := C_Closed;
  590.                                 if tcpc <> nil then begin
  591.                                     oe := TCPRelease(tcpc);
  592.                                 end;
  593.                                 DestroyConnection(rcp);
  594.                             end;
  595.                             otherwise
  596.                                 ;
  597.                         end;
  598.                     end;
  599.                     otherwise
  600.                         ;
  601.                 end;
  602.  
  603.                 if (cer.event = C_NoEvent) & (TickCount > Heartbeat) then begin
  604.                     cer.event := C_HeartBeat;
  605.                     Heartbeat := TickCount + period;
  606.                 end;
  607.             end;{with}
  608.         end;{HandleConnection}
  609.         var
  610.             oci: connectionIndex;
  611.     begin
  612.         cer.event := C_NoEvent;
  613.         if cp <> any_connection then begin
  614.             if ValidConnection(cp) then begin
  615.                 HandleConnection(cp);
  616.             end;
  617.         end
  618.         else begin
  619.             oci := connectionItem;
  620.             repeat
  621.                 if connections[connectionItem].magic = TCPCMagic then begin
  622.                     HandleConnection(connectionItem);
  623.                 end;{if}
  624.                 if connectionItem = max_connections then begin
  625.                     connectionItem := 1;
  626.                 end else begin
  627.                     connectionItem := connectionItem + 1;
  628.                 end;
  629.             until (oci = connectionItem) or (cer.event <> C_NoEvent);
  630.         end;{if}
  631.         GetConnectionEvent := cer.event <> C_NoEvent;
  632.     end;{GetConnectionEvent}
  633.  
  634. end.