Delphi中如何通过Socket收发TList<TRect>类型的RectList对象
实现思路
因为TRect是固定大小的值类型,TList<TRect>内部存储的是连续的TRect结构数组,因此不需要复杂的对象序列化逻辑,只需要按照「先传元素数量、再传所有矩形二进制数据」的约定传输,接收端按照同样规则解析即可还原出同类型变量。
服务端(TServerSocket)发送实现
在需要触发发送的位置编写如下逻辑:
procedure TForm1.btnSendClick(Sender: TObject); var ListCount: Integer; SendBuf: TBytes; begin if RectList.Count = 0 then Exit; if ServerSocket1.Socket.ActiveConnections = 0 then Exit; // 构造发送缓冲区:前4字节存元素数量,后续存储所有TRect数据 ListCount := RectList.Count; SetLength(SendBuf, SizeOf(Integer) + ListCount * SizeOf(TRect)); // 拷贝元素数量到缓冲区头部 Move(ListCount, SendBuf[0], SizeOf(Integer)); // 拷贝所有矩形数据到缓冲区 if ListCount > 0 then Move(RectList.List^, SendBuf[SizeOf(Integer)], ListCount * SizeOf(TRect)); // 发送给第一个连接的客户端,多客户端场景可遍历Connections列表批量发送 ServerSocket1.Socket.Connections[0].SendBuf(SendBuf[0], Length(SendBuf)); end;
客户端(TClientSocket)接收实现
首先在客户端窗体类中新增私有成员,用于处理TCP流的粘包/半包问题:
private FRecvBuf: TBytes; // 全局接收缓冲区 FExpectedCount: Integer; // 待接收的矩形数量,-1表示未收到长度头
窗体初始化时对变量赋值:
procedure TClientForm.FormCreate(Sender: TObject); begin FExpectedCount := -1; SetLength(FRecvBuf, 0); ClientSocket1.Active := True; end;
在TClientSocket的OnRead事件中编写解析逻辑:
procedure TClientForm.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var RecvLen: Integer; TempBuf: array[0..4095] of Byte; P: PByte; NewRectList: TList<TRect>; begin // 读取当前Socket可读的数据 RecvLen := Socket.ReceiveBuf(TempBuf, SizeOf(TempBuf)); if RecvLen <= 0 then Exit; // 新数据追加到全局接收缓冲区 SetLength(FRecvBuf, Length(FRecvBuf) + RecvLen); Move(TempBuf, FRecvBuf[Length(FRecvBuf) - RecvLen], RecvLen); P := @FRecvBuf[0]; while True do begin // 先解析4字节的长度头 if FExpectedCount = -1 then begin if Length(FRecvBuf) < SizeOf(Integer) then Break; // 长度不足,等待后续数据 Move(P^, FExpectedCount, SizeOf(Integer)); Inc(P, SizeOf(Integer)); Delete(FRecvBuf, 0, SizeOf(Integer)); // 移除已处理的长度头 end; // 解析矩形数据 if FExpectedCount >= 0 then begin if Length(FRecvBuf) < FExpectedCount * SizeOf(TRect) then Break; // 数据不足,等待后续数据 // 构造还原后的TList<TRect> NewRectList := TList<TRect>.Create; NewRectList.Count := FExpectedCount; // 预先分配空间 Move(P^, NewRectList.List^, FExpectedCount * SizeOf(TRect)); // 此处可将NewRectList赋值给你的业务变量使用,用完记得释放 // 清理已处理的数据,重置状态 Delete(FRecvBuf, 0, FExpectedCount * SizeOf(TRect)); FExpectedCount := -1; NewRectList.Free; // 若已赋值给全局变量可删除此行 end; end; end;
注意事项
- 该方案仅适用于两端均为Delphi开发的Windows场景,Windows平台下所有Delphi版本的
TRect内存布局完全一致(4个Longint字段,顺序为Left/Top/Right/Bottom,大小16字节) - 若需要跨平台/跨语言通信,可在发送时将每个
TRect的四个字段转为网络字节序,接收后转回本地字节序即可 - 示例仅演示单个客户端的收发逻辑,多客户端场景遍历
ServerSocket.Socket.Connections列表批量发送即可
内容的提问来源于stack exchange,提问作者user13342561
相关产品推荐
相关产品推荐

