我正在使用Delphi中的TIdTCPClient / TIdTcpServer indy组件编写客户端 - 服务器应用程序。
数据传输通常工作正常,但我经常从服务器读取错误的数据;我收到了之前的要求'答案,而不是现在的答案。
在调试期间,两个应用程序都在本地工作,因此在传输过程中无法丢失数据。
超时为1000-3000毫秒,这远远不足以避免在收到第一个请求之前发送第二个请求。
我使用简单的数据格式:前4个字节是数据包长度,其余是该长度的二进制数据。
服务器端代码是(仅用于发送字符串;我也使用二进制缓冲区,但是这段代码更容易理解和检查):
Var
lng: LongInt;
ib: TIdBytes;
begin
// Prepare data to send:
lng:=length(s);// s is an AnsiString to be sent
SetLength(ib,lng+4);
Move(lng,ib[0],4);
Move(s[1],ib[4],length(s));
// Send:
AContext.Connection.IOHandler.WriteDirect(ib);
end;
发送请求的客户端代码是相同的(在最后一行中调用TIdTcpClient.IOHandler.WriteDirect())。 用于读取服务器答案的客户端代码是:
Var
ib: TIdBytes;
size,done,lng: LongInt;
begin
Result:=false;
// answer length:
try
SetLength(ib,0);
tcp.IOHandler.ReadBytes(ib,4,false);
Move(ib[0],size,4);
if length(ib)<0 then Exit;// wrong data
except
on E: Exception do;// code skipped
end;
// read answer body:
done:=0;
b.Clear;// b is my buffer, TStream descendant
while done<size do
begin
lng:=Min(size-done,MaxBlockSize);
// read:
SetLength(ib,0);// to be sure
tcp.IOHandler.ReadBytes(ib,lng,false);
if length(ib)=0 then Exit;// error reading
// append my buffer:
b.Wr(ib[0],length(ib));
// progress:
Inc(done,length(ib));
end;
end;
数据交换顺序为:
客户端向服务器发送请求,
服务器读取请求并将回复发送回客户端,
客户阅读答案。
步骤3中显示错误的数据。
也许我做错了什么?
在向服务器发送请求以清除传入缓冲区之前,我已经尝试过ReadBytes(),但这并没有帮助,就像我尝试过的许多其他事情一样......
现在我只是出于想法:(
答案 0 :(得分:2)
您的I / O逻辑比它需要的复杂得多,尤其是在客户端。您是手动执行Indy可以自动执行的操作。
在客户端,由于您将数据保存到TStream中,因此可以让Indy直接将数据读入TStream:
begin
...
b.Clear;// b is my buffer, TStream descendant
// ReadStream() can read a '<length><bytes>' formatted
// message. When its ASize parameter is -1 and its
// AReadUntilDisconnect parameter is False, it reads
// the first 4 or 8 bytes (depending on the LargeStream
// property) and interprets them as the byte count,
// in network byte order...
tcp.IOHandler.RecvBufferSize := MaxBlockSize;
tcp.IOHandler.LargeStream := False; // read 4-byte length
// read answer:
try
tcp.IOHandler.ReadStream(b, -1, false);
except
on E: Exception do begin
// the socket is now in an indeterminate state.
// You do not know where the reading left off.
// The only sensible thing to do is disconnect
// and reconnect...
tcp.Disconnect;
...
end;
end;
...
end;
在服务器端,您可以通过两种不同的方式发送与上述代码兼容的消息:
var
lng: LongInt;
ib: TIdBytes;
begin
// Prepare data to send:
// s is an AnsiString to be sent
lng := Length(s);
SetLength(ib, lng);
Move(PAnsiChar(s)^, PByte(ib)^, lng);
// Send:
AContext.Connection.IOHandler.Write(lng); // send 4-byte length, in network byte order
AContext.Connection.IOHandler.Write(ib); // send bytes
end;
或者:
var
strm: TIdMemoryBufferStream;
begin
// Prepare data to send:
// s is an AnsiString to be sent
strm := TIdMemoryBufferStream.Create(PAnsiChar(s), Length(s));
try
// Send:
// Write(TStream) can send a '<length><bytes>' formatted
// message. When its ASize parameter is 0, it sends the
// entire stream, and when its AWriteByteCount parameter
// is True, it first sends the byte count as 4 or 8 bytes
// (depending on the LargeStream property), in network
// byte order...
AContext.Connection.IOHandler.LargeStream := False; // send 4-byte lengtb
AContext.Connection.IOHandler.Write(strm, 0, True);
finally
strm.Free;
end;
end;
正如您所看到的,此代码发送的是您最初发送的相同类型的消息,更改的是管理消息的代码。此外,它强制消息字节计数以网络字节顺序发送,而您以主机字节顺序发送它。为了保持一致性和多平台兼容性,应尽可能以网络字节顺序发送多字节整数。