Connection Closed Gracely Indy TCPServer移动应用程序Delphi XE8

时间:2015-09-30 15:58:44

标签: delphi exception mobile indy10 delphi-xe8

我正在使用Indy TCPClient / TCPServer来验证移动设备的注册。这个过程非常简单,我在服务器端读取标识符,根据数据库控制文件对其进行验证,并将响应发送回客户端。

大部分内容似乎都能正常工作,但我会定期在服务器端获得EIDConnClosedGracefully异常。我似乎无法精确确定连接被错误关闭的位置。实际上,似乎服务器在不应该执行readln时(在Connection关闭之后)并且我不知道为什么。可能我没有正确同步。我在我的工具/选项/调试器选项中设置了忽略Indy Silent Exceptions但我想知道是什么引发了异常。我可以执行4或5次注册功能,然后抛出异常但是它非常不一致。

任何建议都将不胜感激。

以下是我的代码:

服务器

  try
    MIRec.RecType := AContext.Connection.IOHandler.ReadLn;

    if (MIRec.RecType = 'I')
    or (MIRec.RecType = 'R') then
    begin
// Verify the connecting device is 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') then
         LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
      else
      begin
// Register the Device in STIKS
      if qryMobileDevice.EOF then
      begin
        LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);

// If Record Does not exist Add to the 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 and registration refused.
        if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
           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 do
    begin
      Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message);
    end;
  end;

客户端

  if MessageDlg('Register Device With Server?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbNo, TMsgDlgBtn.mbYes], 0) = mrNo then Exit;

  try
    IdTCPClient1.Connect;
    try
      MainForm.IdTCPClient1.IOHandler.WriteLn('R'); // Tell Server we are sending a Registration Record

      Device := TUIDevice.Wrap(TUIDevice.OCClass.currentDevice);
      IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.identifierForVendor.UUIDString));
      Registered := IdTCPClient1.IOHandler.ReadLn;  // Get response from server
      Authenticated := (Registered = 'T');
      IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.Name));
    finally
      IdTCPClient1.DisConnect;

      if Registered <> 'T' then
         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 do
    begin
        MessageDlg('** An error occurred Registering Device ' + #13#10 + 'With a message: ' + E.Message, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
    end;
  end;

我的日志输出。

A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:36:54 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:00 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:04 AM
** An error occurred Receiving File 
With a message: Connection Closed Gracefully.
A Client Disconnected

1 个答案:

答案 0 :(得分:7)

服务器代码是OnConnect还是OnExecute事件?

假设OnExecute,即循环事件,它会在连接的生命周期内连续循环。在每次迭代中,您将再次调用ReadLn以从客户端读取下一个命令。如果客户端断开连接,则必须返回套接字以获取更多数据的下一次读取(在IOHandler.InputBuffer已用尽之后)将相应地引发异常。这是正常行为,以及Indy如何运作。

真正的问题是你有一个异常处理程序,它无条件地将所有异常记录为错误,甚至是正常的断开连接。在向备忘录添加错误消息时,您的异常处理程序不会与UI线程同步,并且它不会重新引发任何捕获的Indy异常,因此TIdTCPServer可以根据需要处理它(例如停止{{} 1}}循环并触发OnExecute事件。)

尝试更像这样的东西:

OnDisconnect

另一方面,我建议完全删除// if registration is only done once, this code should // be in the OnConnect event instead... procedure TMyForm.MyTCPServerExecute(AContext: TIdContext); begin try MIRec.RecType := AContext.Connection.IOHandler.ReadLn; if (MIRec.RecType = 'I') or (MIRec.RecType = 'R') then begin // Verify the connecting device is 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') then LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName else begin // Register the Device in STIKS if qryMobileDevice.EOF then begin LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now); // If Record Does not exist Add to the 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 and registration refused. if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then 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 do begin if not (E is EIdSilentException) then begin TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message); end ); end; // optionally remove the below 'if' to close the // connection on any exception, including DB errors, etc... if E is EIdException then raise; end; end; end; 并使用服务器的try/except事件。让任何异常关闭连接,然后在最后记录原因:

OnException

顺便说一句,将procedure TMyForm.MyTCPServerExecute(AContext: TIdContext); begin MIRec.RecType := AContext.Connection.IOHandler.ReadLn; if (MIRec.RecType = 'I') or (MIRec.RecType = 'R') then begin // Verify the connecting device is 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') then LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName else begin // Register the Device in STIKS if qryMobileDevice.EOF then begin LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now); // If Record Does not exist Add to the 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 and registration refused. if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then 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 is EIdSilentException) then begin TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('** An error occurred' + sLineBreak + 'With a message: ' + AException.Message); end ); end; end; TThread.Synchronize()一起使用时,您必须非常小心。如果主UI线程忙于在服务器事件处理程序调用{​​{1}}时停用服务器,则UI线程和同步线程之间将发生死锁(主线程正在等待服务器完成停用,但服务器正在等待线程终止,但线程正在等待UI线程完成停用服务器)。对于您所显示的简单日志记录,我建议使用TIdTCPServer来避免任何死锁。或者在工作线程中停用服务器,以便UI线程可以继续处理Synchronize()个请求。

相关问题