为什么我在 Indy Sockets 9 IdTcpServer ServerExecute 中遇到 AccessViolation?
-
06-07-2019 - |
题
第一个问题:
以下例程是 Indy 9 IdTcpServer.OnExecute 例程的正确实现吗?
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;
第二个问题(实际上更重要):
现在偶尔会出现访问冲突(从地址 000000 读取)。显然在这条线上:
AContext.Connection.ReadFromStack(True, 600, False);
但检查 AContext / Connection / InputBuffer / IOHandler = nil BEFORE 是否为 false。调用之后(并且引发异常之后)IOHandler 为零。
我们使用 RAD Studio / Delphi 2007。
解决方案
IOHandler可以像你描述的那样成为nil的唯一方法是你的应用程序中的另一个线程在你的工作线程仍在运行时在连接上调用Disconnect()。
其他提示
嗯,我拥有的最简单的 onExecute 处理程序是这样的。(请原谅 C++ 而不是 Delphi,但你会明白的。
void __fastcall MyPanel::DoTCPExecute(TIdPeerThread * AThread)
{
AnsiString text =AThread->Connection->ReadLn();
// now do something with text
}
我能看到的唯一明显的问题是您试图使用数据的“计时”来确定何时拥有完整的字符串。这是 TCP 真正的禁忌。您可能只有字符串的第一个字节,或者可能同时发送多个字符串。对于 TCP,无法保证每个“发送”最终都会在另一端作为单个“接收”而结束。
您需要以其他方式“定界”字符串。Readln 使用换行符作为终止符 - 另一种方法是为每个数据块添加长度字段前缀。您读取长度,然后读取剩余的数据。
代码就是这样,但我认为这不是一个干净的选择:
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;
不隶属于 StackOverflow