Por que recebo um AccessViolation na Indy Sockets 9 IdTcpServer ServerExecute?
-
06-07-2019 - |
Pergunta
Primeira Pergunta:
É o seguinte rotina de uma correta aplicação de um Indy 9 IdTcpServer.OnExecute rotina?
procedure TMyConnServer.ServerExecute(AContext: TIdPeerThread);
var
buffSize: integer;
str: string;
begin
AContext.Connection.ReadFromStack(True, 600, False);
buffSize := AContext.Connection.InputBuffer.Size;
if (buffSize > 0) then
{ Extract input buffer as string }
str := AContext.Connection.ReadString(buffSize);
{ Notify connection object of received data }
if (AContext.Data <> nil) then
begin
TConnectionHandler(AContext.Data).Read(str);
end;
end;
end;
Segundo (na verdade, o mais importante) Pergunta:
Agora há, ocasionalmente, uma violação de acesso (leitura de endereço 000000).Obviamente, na linha:
AContext.Connection.ReadFromStack(True, 600, False);
mas de verificar se AContext / Conexão / InputBuffer / IOHandler = nil ANTES, é falso.APÓS a chamada (e depois a exceção foi gerada) o IOHandler é nula.
Estamos usando RAD Studio / Delphi 2007.
Solução
A única forma de IOHandler pode tornar-se nulo, como você descreve, se outro thread no seu aplicativo chamado Desligar() a ligação enquanto o thread de trabalho ainda estava correndo.
Outras dicas
Bem, o manipulador Onexecute mais simples que tenho é assim. (Desculpe C ++ em vez de Delphi, mas você terá a ideia.
void __fastcall MyPanel::DoTCPExecute(TIdPeerThread * AThread)
{
AnsiString text =AThread->Connection->ReadLn();
// now do something with text
}
O único problema óbvio que posso ver é que você está tentando usar o "tempo" dos dados para determinar quando você tem uma string completa. Este é um verdadeiro não-não com TCP. Você pode ter o primeiro byte de uma corda, ou pode ter várias strings enviadas de uma só vez. Com o TCP, não há garantia de que cada "envio" termine como único "receber" na outra extremidade.
Você precisa "delimitar" sua string de outra maneira. O Readln usa o caractere nova linha como terminador - outra abordagem é prefixar cada pedaço de dados com um campo de comprimento. Você lê o comprimento e lê os dados restantes.
O código funciona assim, mas não acho que seja uma opção limpa:
if (AContext.Connection.Connected) then
begin
try
AContext.Connection.ReadFromStack(false, 1, false);
except on E: EAccessViolation do
// ignore
end;
end;
buffSize := AContext.Connection.InputBuffer.Size;