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
Post a Comment