当使用Delphi通过TCP协议传输JSON格式的数据,并且每包数据的长度不固定时,处理分包和多包的情况变得更加复杂。在这种情况下,通常的做法是在每个JSON消息前后添加特定的分隔符(如`{}`、`\n`等),以便接收端能够正确地识别和解析这些消息。
以下是一个示例,展示了如何在Delphi中实现这一机制:
1. 发送端:在每个JSON消息后附加一个分隔符(例如换行符 `\n`)。
2. 接收端:读取数据并根据分隔符来分割和解析JSON消息。
发送端代码
uses
System.SysUtils, IdTCPClient, System.JSON;
procedure SendJsonData(const AClient: TIdTCPClient; const AJsonObject: TJSONObject);
var
JsonString: string;
begin
// 将JSON对象转换为字符串
JsonString := AJsonObject.ToString;
// 发送消息内容,并在末尾添加换行符作为分隔符
AClient.IOHandler.WriteLn(JsonString);
end;
// 示例调用
procedure TForm1.ButtonSendClick(Sender: TObject);
var
JsonObject: TJSONObject;
begin
JsonObject := TJSONObject.Create;
try
JsonObject.AddPair('key1', 'value1');
JsonObject.AddPair('key2', 'value2');
SendJsonData(IdTCPClient1, JsonObject);
finally
JsonObject.Free;
end;
end;
接收端代码
uses
System.SysUtils, IdTCPServer, System.JSON, System.Classes;
type
TMessageBuffer = class
private
FBuffer: TStringList;
public
constructor Create;
destructor Destroy; override;
procedure AddData(const Data: string);
function GetCompleteMessages: TArray<string>;
end;
constructor TMessageBuffer.Create;
begin
inherited Create;
FBuffer := TStringList.Create;
end;
destructor TMessageBuffer.Destroy;
begin
FBuffer.Free;
inherited Destroy;
end;
procedure TMessageBuffer.AddData(const Data: string);
begin
FBuffer.Text := FBuffer.Text + Data;
end;
function TMessageBuffer.GetCompleteMessages: TArray<string>;
var
Lines: TStringDynArray;
I: Integer;
CompleteMessages: TArray<string>;
begin
SetLength(CompleteMessages, 0);
// 分割缓冲区中的每一行
Lines := SplitString(FBuffer.Text, #13#10); // 处理Windows风格的换行符
if Length(Lines) > 0 then
begin
for I := Low(Lines) to High(Lines) do
begin
if Trim(Lines[I]) <> '' then
begin
SetLength(CompleteMessages, Length(CompleteMessages) + 1);
CompleteMessages[High(CompleteMessages)] := Lines[I];
end;
end;
// 清空已处理的部分
FBuffer.Clear;
end;
Result := CompleteMessages;
end;
procedure ReceiveJsonData(AContext: TIdContext; AMessageBuffer: TMessageBuffer);
var
Data: string;
Messages: TArray<string>;
I: Integer;
JsonObject: TJSONObject;
begin
// 读取可用数据
Data := AContext.Connection.IOHandler.ReadLnWait(-1, False);
// 添加数据到缓冲区
AMessageBuffer.AddData(Data);
// 获取完整的JSON消息
Messages := AMessageBuffer.GetCompleteMessages;
// 解析每个完整的消息
for I := Low(Messages) to High(Messages) do
begin
try
JsonObject := TJSONObject.ParseJSONValue(Messages[I]) as TJSONObject;
try
Writeln('Received JSON message: ' + JsonObject.ToString);
// 处理解析后的JSON对象
finally
JsonObject.Free;
end;
except
on E: Exception do
Writeln('Error parsing JSON message: ' + E.Message);
end;
end;
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
ReceiveJsonData(AContext, FMessageBuffer);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FMessageBuffer := TMessageBuffer.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FMessageBuffer.Free;
end;
完整代码解释
1. 发送端:
- `SendJsonData` 函数负责将JSON对象转换为字符串,并在末尾添加换行符 `\n` 作为分隔符。
- 这样可以确保每个JSON消息都是独立的,并且可以通过换行符进行分割。
2. 接收端:
- 使用 `TMessageBuffer` 类来管理接收到的数据缓冲区。
- `AddData` 方法用于将新接收到的数据添加到缓冲区中。
- `GetCompleteMessages` 方法用于从缓冲区中提取完整的JSON消息。它会根据换行符分割缓冲区中的数据,并返回完整的JSON消息数组。
- 在 `ReceiveJsonData` 函数中,首先读取可用的数据并将其添加到缓冲区中。
- 然后调用 `GetCompleteMessages` 方法获取完整的JSON消息,并逐个解析这些消息。
- 使用 `TJSONObject.ParseJSONValue` 方法将字符串解析为JSON对象,并处理解析后的JSON对象。
这种方法可以有效地处理多包数据的情况,确保每次接收到的是一个完整的JSON消息。请根据具体需求调整代码中的细节。