exception - Connection Closed Gracefully Indy TCPServer Mobile App Delphi XE8 -


i using indy tcpclient/tcpserver verify registration of mobile device. process straight forward read identifier on server side, validate against database control file , send response client.

everything appears work correctly part periodically eidconnclosedgracefully exception on server side. can't seem pinpoint connection being closed improperly. in fact, appears server executing readln when not supposed (after connection closed) , not know why. possibly not synchronizing properly. have indy silent exceptions set ignore in tools/options/debugger options know throwing exception. can execute registration function 4 or 5 times , exception thrown inconsistent.

any suggestions appreciated.

following code:

server

  try     mirec.rectype := acontext.connection.iohandler.readln;      if (mirec.rectype = 'i')     or (mirec.rectype = 'r')     begin // verify connecting device registered       mirec.identifier := acontext.connection.iohandler.readln;        qrymobiledevice.close;       qrymobiledevice.parameters.parambyname('identifier').value := mirec.identifier;       qrymobiledevice.open;        acontext.connection.iohandler.writeln(qrymobiledevice.findfield('active').asstring);        mirec.devicename := acontext.connection.iohandler.readln;       if (mirec.rectype = 'i')          logentry := 'a connection has been established with: ' + mirec.devicename       else       begin // register device in stiks       if qrymobiledevice.eof       begin         logentry := 'registering: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);  // if record not exist add control file;         nextid := getnextid('next_mobile_device_id');          qrymobiledevice.insert;         qrymobiledevice.fieldbyname('mobile_device_id').value := nextid;         qrymobiledevice.fieldbyname('identifier').value := mirec.identifier;         qrymobiledevice.fieldbyname('description').value := mirec.text;         qrymobiledevice.fieldbyname('active').value := 't';         qrymobiledevice.fieldbyname('operator_saved').value := 'from app';         qrymobiledevice.fieldbyname('date_saved').value := now;         qrymobiledevice.post;       end       else       begin // device has been flagged , registration refused.         if qrymobiledevice.findfield('active').asstring = 't'            logentry := '** registration successful: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now)         else            logentry := '** registration refused: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);       end;        qrymobiledevice.close;     end;      tthread.synchronize(nil,       procedure       begin         memo1.lines.add(logentry);       end);     end;   except     on e: exception     begin       memo1.lines.add('** error occurred receiving file ' + #13#10 + 'with message: ' + e.message);     end;   end; 

client

  if messagedlg('register device server?', tmsgdlgtype.mtconfirmation, [tmsgdlgbtn.mbno, tmsgdlgbtn.mbyes], 0) = mrno exit;    try     idtcpclient1.connect;     try       mainform.idtcpclient1.iohandler.writeln('r'); // tell server sending registration record        device := tuidevice.wrap(tuidevice.occlass.currentdevice);       idtcpclient1.iohandler.writeln(nsstrtostr(device.identifierforvendor.uuidstring));       registered := idtcpclient1.iohandler.readln;  // response server       authenticated := (registered = 't');       idtcpclient1.iohandler.writeln(nsstrtostr(device.name));           idtcpclient1.disconnect;        if registered <> 't'          messagedlg('registration failed!', tmsgdlgtype.mtinformation, [tmsgdlgbtn.mbok], 0)       else       begin         authenticated := true;         messagedlg('registration has completed successfully!', tmsgdlgtype.mtinformation, [tmsgdlgbtn.mbok], 0);       end;     end;   except     on e: exception     begin         messagedlg('** error occurred registering device ' + #13#10 + 'with message: ' + e.message, tmsgdlgtype.mtinformation, [tmsgdlgbtn.mbok], 0);     end;   end; 

output log.

a client connected ** registration successful: ; 7ffc0274-afb1-4e35-b8d9-f987b587804d; wed. september 30/2015, 9:36:54 client disconnected client connected ** registration successful: ; 7ffc0274-afb1-4e35-b8d9-f987b587804d; wed. september 30/2015, 9:37:00 client disconnected client connected ** registration successful: ; 7ffc0274-afb1-4e35-b8d9-f987b587804d; wed. september 30/2015, 9:37:04 ** error occurred receiving file  message: connection closed gracefully. client disconnected 

is server code in onconnect or onexecute event?

assuming onexecute, looped event, loops continuously lifetime of connection. on each iteration, calling readln again read next command client. if client disconnects, next read has go socket more data (after iohandler.inputbuffer has been exhausted) raise exception accordingly. normal behavior, , how indy designed operate.

the real problem have exception handler unconditionally logging exceptions errors, graceful disconnects. , exception handler not synchronizing ui thread when adding error message memo, , not re-raising caught indy exception tidtcpserver can handle needed (such stop onexecute loop , trigger ondisconnect event).

try more instead:

// if registration done once, code should // in onconnect event instead...  procedure tmyform.mytcpserverexecute(acontext: tidcontext); begin   try     mirec.rectype := acontext.connection.iohandler.readln;      if (mirec.rectype = 'i') or        (mirec.rectype = 'r')     begin       // verify connecting device registered       mirec.identifier := acontext.connection.iohandler.readln;        qrymobiledevice.close;       qrymobiledevice.parameters.parambyname('identifier').value := mirec.identifier;       qrymobiledevice.open;        acontext.connection.iohandler.writeln(qrymobiledevice.findfield('active').asstring);        mirec.devicename := acontext.connection.iohandler.readln;       if (mirec.rectype = 'i')         logentry := 'a connection has been established with: ' + mirec.devicename       else       begin         // register device in stiks         if qrymobiledevice.eof         begin           logentry := 'registering: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);            // if record not exist add control file;           nextid := getnextid('next_mobile_device_id');            qrymobiledevice.insert;           qrymobiledevice.fieldbyname('mobile_device_id').value := nextid;           qrymobiledevice.fieldbyname('identifier').value := mirec.identifier;           qrymobiledevice.fieldbyname('description').value := mirec.text;           qrymobiledevice.fieldbyname('active').value := 't';           qrymobiledevice.fieldbyname('operator_saved').value := 'from app';           qrymobiledevice.fieldbyname('date_saved').value := now;           qrymobiledevice.post;         end         else         begin           // device has been flagged , registration refused.           if qrymobiledevice.findfield('active').asstring = 't'             logentry := '** registration successful: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now)           else             logentry := '** registration refused: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);         end;          qrymobiledevice.close;       end;        tthread.synchronize(nil,         procedure         begin           memo1.lines.add(logentry);         end       );     end;   except     on e: exception     begin       if not (e eidsilentexception)       begin         tthread.synchronize(nil,           procedure           begin             memo1.lines.add('** error occurred receiving file ' + #13#10 + 'with message: ' + e.message);           end         );       end;        // optionally remove below 'if' close       // connection on exception, including db errors, etc...       if e eidexception         raise;     end;   end; end; 

on other hand, suggest getting rid of try/except altogether , use server's onexception event instead. let exception close connection, , log why @ end:

procedure tmyform.mytcpserverexecute(acontext: tidcontext); begin   mirec.rectype := acontext.connection.iohandler.readln;    if (mirec.rectype = 'i') or      (mirec.rectype = 'r')   begin     // verify connecting device registered     mirec.identifier := acontext.connection.iohandler.readln;      qrymobiledevice.close;     qrymobiledevice.parameters.parambyname('identifier').value := mirec.identifier;     qrymobiledevice.open;      acontext.connection.iohandler.writeln(qrymobiledevice.findfield('active').asstring);      mirec.devicename := acontext.connection.iohandler.readln;     if (mirec.rectype = 'i')       logentry := 'a connection has been established with: ' + mirec.devicename     else     begin       // register device in stiks       if qrymobiledevice.eof       begin         logentry := 'registering: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);          // if record not exist add control file;         nextid := getnextid('next_mobile_device_id');          qrymobiledevice.insert;         qrymobiledevice.fieldbyname('mobile_device_id').value := nextid;         qrymobiledevice.fieldbyname('identifier').value := mirec.identifier;         qrymobiledevice.fieldbyname('description').value := mirec.text;         qrymobiledevice.fieldbyname('active').value := 't';         qrymobiledevice.fieldbyname('operator_saved').value := 'from app';         qrymobiledevice.fieldbyname('date_saved').value := now;         qrymobiledevice.post;       end       else       begin         // device has been flagged , registration refused.         if qrymobiledevice.findfield('active').asstring = 't'           logentry := '** registration successful: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now)         else           logentry := '** registration refused: ' + mirec.text + '; ' + mirec.identifier + '; ' + formatdatetime('ddd. mmmm d/yyyy, h:mm:ss am/pm', now);       end;        qrymobiledevice.close;     end;      tthread.synchronize(nil,       procedure       begin         memo1.lines.add(logentry);       end     );   end; end;  procedure tmyform.mytcpserverexception(acontext: tidcontext; aexception: exception); begin   if not (aexception eidsilentexception)   begin     tthread.synchronize(nil,       procedure       begin         memo1.lines.add('** error occurred' + slinebreak + 'with message: ' + aexception.message);       end     );   end; end; 

btw, have careful when using tthread.synchronize() tidtcpserver. if main ui thread busy deactivating server when server event handler calls synchronize(), deadlock occur between ui thread , synchronizing thread (the main thread waiting server finish deactivating, server waiting thread terminate, thread waiting ui thread finish deactivating server). simple logging have shown, suggest using tthread.queue() instead avoid deadlock potential. or else deactivate server in worker thread ui thread free continue processing synchronize() requests.


Comments

Popular posts from this blog

html - Outlook 2010 Anchor (url/address/link) -

javascript - Why does running this loop 9 times take 100x longer than running it 8 times? -

Getting gateway time-out Rails app with Nginx + Puma running on Digital Ocean -