FSC-0010 RESYNC, a sealink protocol enhancement by Henk Wevers 2/0 ========================================================== What is resync (recovarable sealink) ------------------------------------ Resync is a protocol enhancement on Sealink by Sea corporation that allows the protocol to pickup broken transfers were it was interrupted. The coding overhead is very minor because almost all routines needed are already part of most sealink implementations. As a sideeffect transmissions of exact duplicate files (from whatever source) will only result in the two programs exchanging EOT and thus saving a lot of transfertime and costs. The protocol ------------- The capability of doing ackless sealink is signalled by the SENDER by having byte 41 (1 based) in block 0 of a sealink file transfer being <> 0. Recovery is signalled in the sameway by byte 42 <> 0. Recoverable sealink starts off like normal (unrecoverable sealink). After the receiver has received block zero without errors the receiver checks for a duplicate filename in its incoming file directory. When a match is found the time and datestamp are checked and when they match the resync procedure is started otherwise` the transfer goes on like normal. Recovery procedure: RECEIVER -------- The receiver sends the following block to the sender: sync = $22 blocknumber: ascii , number of block to resume with, 1 based eot = $03 crc as usual The reason this form is choosen is that it is the same block as used for passing the filename in sealink based filerequests so the code was already there. now the receiver waits ontil the line dies (looks for a 1 sec pause) then sends $05 and waits for ACK or NAK from the sender. If nak is received the recovery procedure is restarted . After a given number of failed tries the session is aborted. After an ACK the receiver 'seeks' at the given block and resumes sealink transfer. SENDER ------ The sender has the capability to recognize returning ACK, NAK and SYNC. When a SYNC is received the sender stops all output, purges its outputbuffers and tries to get the resyncinfo. (some smart programming to allow an unintended sync caused by linenoise may make the protocol more stable. You may also test for ack/nack directly after the SYNC because the ascvii blocknumer garantees that a received ack/nak probably means a spurious sync. ). As soon as the blocknumber is received the sender acks and resume the sealink transfer at the given block. NOTES ------ This only works if the receiver closes a partly recived file properly, gives it the right name and sets the right time/date. In the current dutchie 2.80 implementation it also only works for files, not for mailpackets, but that is only a question of implementation and choise. IMPLEMENTATION --------------- Currently only dutchie 2.80 implements this enhancement. testing has shown that the protocol is very stable and works well. Some code in turbo pascal follows to help those who want to implement it. 1. The code used for transferring the wanted restart blocknumber to the sender. In real implementations this code will be shared by the filerequest stuff. function resyncok(blknum:integer):boolean; Var blockstring : string[5]; tries, ch, n : Integer; Begin str(blknum,blockstring); tries := 0; repeat tries := tries +1; if ((not Comm_Carrier(Comport)) or keyescape or ( tries >=8)) then begin If not Comm_Carrier(Comport) then Logit(3,1,'Lost Carrier') else If (tries>=8) then Logit(3,1,'Too much errors') else Logit(3,1,'Keyboard '); dumerr := fileerr; resyncok := false; exit; end; Comm_purge_in(ComPort); ClearCrC; comm_transmit(comport,22); For n:= 1 to length(blockstring) do Begin Comm_transmit(Comport,Ord(blockstring[n])); UpdatCrc(ord(blockstring[n])); End; UpdatCrc(0); UpdatCrc(0); Comm_Transmit(Comport,$03); Comm_Transmit(ComPort,Lo(CrcAccum)); Comm_Transmit(ComPort,Hi(CrcAccum)); Comm_purge_in(comport); {wait for a 1 sec pause} {Wait until line dies} Repeat Ch := timed_read(ComPort, 10); Until (Ch = $FFFF); comm_transmit(comport,05); ch := timed_read(Comport,20); until (ch=ACK); resyncok := true; end; 2. part of sender ack/nack logic to handshake above code function getsyncblock(var c:integer):Boolean; var t1 : real; n, Crclo, CrcHi, pl, code, ch : integer; temp1, temp : string64; label 100; begin ReqName := ''; getsyncblock := false; t1 := timerset(50); repeat ch := timed_read(comport,0); if ((ch > $1F) and (ch <$7F)) then ReqName := ReqName + Chr(ch); if ((ch = ack) or (ch = nak)) then begin c:= ch; goto 100; end; if not comm_carrier(Comport) then goto 100; until ((ch = $03) or timeup(t1)); CrcLo := Timed_Read(Comport,10); CrcHi := Timed_Read(Comport,10); ClearCrc; For n := 1 to length(ReqName) do UpdatCrc(ord(reqName[n])); UpdatCrc(0); UpdatCrc(0); {now wait for enquiry (must be within 5 secs)} t1 := timerset(50); repeat ch := timed_read(comport,50); until ((ch = $05) or timeup(t1)); If ((Lo(CrcAccum) = CrcLo) and (Hi(CrcAccum) = CrcHi)) then Begin val(reqname,outblk,pl); Comm_transmit(Comport,ACK); getsyncblock :=true; end else begin fixwindow; Writeln(' Bad Checksum'); Comm_transmit(comport,Nak); end; 100: end; Procedure AckChk; { The Various ACK/NAK states are: 0: Ground state, ACK or NAK expected 1: ACK received 2: NAK received 3: ACK, bloknumber received 4: NAK, bloknumber received } Var c : Integer; label 100; Begin ackrep := false; c := timed_read(ComPort,0); While c <> $FFFF Do Begin If ((Ackst = 3) Or (Ackst = 4)) Then Begin Slide := 1; If (Rawblk = (c Xor $FF)) Then Begin Rawblk := Outblk-((Outblk-Rawblk) And $FF); If ((Rawblk >= 0) And (Rawblk <= Outblk) And (Rawblk > (Outblk-128))) Then Begin If (Ackst = 3) Then {advance for an ACK} Begin If (Ackblk <= Rawblk) Then Ackblk := Rawblk; Slide := SeaWindow; ackseen := ackseen + 1; if (ackless and (ackseen > 10)) then begin ackless := false; fixwindow; writeln(#13,'- Overdrive disengaged '); end; fixwindow; Write(#13, ' ACK ', Rawblk:5, ' == ') End Else Begin If (Rawblk < 0) Then Outblk := 0 Else Outblk := Rawblk; If numnak < 4 then slide := seawindow else slide := 1; fixwindow; Write(#13, ' NAK ', Rawblk:5, ' == '); End; Ackrep := true; End; End; Ackst := 5; End; If ((Ackst = 1) Or (Ackst = 2)) Then Begin Rawblk := c; Ackst := Ackst+2 End; If (Not(Slide = SeaWindow) Or (Ackst = 0)) Then Begin If (c = syn) then begin Write(#13, ' Resync received ',#13); if not getsyncblock(c) then begin if ((c = ack) or (c=nak)) then goto 100; numnak := 255; exit; end; ackblk := outblk-1; beginblk := outblk-1; end; 100: If (c = Ack) Then Begin If (Not(Slide = SeaWindow)) Then Begin Ackblk := Ackblk+1; fixwindow; Write(#13, ' ACK ', Ackblk:5, ' -- '); ackrep := true; End; Ackst := 1; NumNak := 0; End Else Begin If ((c = Crc) Or (c = Nak)) Then Begin If (Chktec > 1) Then Begin If (c = Nak) Then Chktec := 0 Else Chktec := 1; If (Modem Or Modem7) Then Ackblk := 0; End; Comm_purge_out(Comport); TimeWait(6); If Not(Slide = SeaWindow) Then Begin Outblk := Ackblk+1; fixwindow; Write(#13, ' NAK ', Ackblk+1:5, ' -- '); Ackrep := true; End; Ackst := 2; NumNak := NumNak+1; If BlkSnt > 0 Then Toterr := Toterr+1; End; End; End; If (Ackst = 5) Then Ackst := 0; c := timed_read(ComPort,0); End; End; 3. part of receiver logic ---------------------------- {we come here after successfully receiving block zero} If Sealink then begin Timestring := Seatime((((Buffer[8]*256.0)+Buffer[7])*256.0+Buffer[6])*256.0+Buffer[5]); ackless := false; If (Buffer[41] <> 0) then begin writeln('- Overdrive engaged'); ackless := true; end; If (Buffer[42] <> 0) then begin writeln('- Recovery enabled'); recovers := true; end; end; Assign(Afile, FileDir+filenm); Reset(Afile); If IOResult = 0 Then Begin if sealink and recovers then begin {find date/time} code := FindFirst(Filedir+filenm); If code = 0 Then begin {we have a duplicate ?} If file_name = filenm then begin {check timestamp} tstring[0] := #4; tstring[1] := Chr(dir.time[1]); tstring[2] := Chr(dir.time[2]); tstring[3] := Chr(dir.date[1]); tstring[4] := Chr(dir.date[2]); if tstring = timestring then begin Blknum :=Trunc(file_size/128)+1; startblk := blknum-1; Str(blknum,blkstring); LogIt(3,1, 'Resynced from '+blkstring); if resyncok(blknum) then begin resyncflag := true; longseek(afile,(blknum-1)*128); truncate(afile); end else begin sealinkrx := false; goto 150; end; end; end; end; end; if not resyncflag then begin if not overwrite then begin filenm[1] := '$'; LogIt(3,1, 'Renamed to '+filenm); end else Logit(3,1,'Overwrote old file !'); end; End; PLEASE COMPARE THESE TO THE ORIGINAL SEA DOCUMENTS ON SEALINK (IN C)