读取 TidTCPServer 传入数据直到断开连接
Reading TidTCPServer incoming data until disconnect
我正在尝试从 Delphi 的 TTCP 服务器迁移到 Indy 的 TidTCPServer (Delphi XE10.2),但我不知道如何在执行中读取传入数据,所有示例我发现正在使用需要 "ETX" 字符的 readln。而且我还没有找到接收到的字节数或任何长度。
那么我该如何阅读完整的包裹呢?
我想象的是这样的; 读取字节直到 "disconnect"
我在下面尝试了这个(尽管不是同时),我没有得到任何包或字节,除非我使用 telnet 客户端并从键盘输入字符 - 这将逐字节给我数据.但是来自“第 3 方客户端”的包裹从未出现。我看到客户端再次连接并断开连接。
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
Port : Integer;
PeerPort : Integer;
PeerIP : string;
msgFromClient : string;
msgToClient : string;
buf : TidBytes;
l :integer;
ABufStream : TMemoryStream;
begin
// this doesn't return anything
ABufStream := TMemoryStream.Create;
try
// AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);
AContext.Connection.IOHandler.InputBufferToStream(ABufStream, -1);
ABufStream.Position := 0;
ABufStream.WriteBuffer(buf, ABufStream.Size);
msgFromClient := format('received %d bytes',[ABufStream.Size]);
finally
ABufStream.Free;
end;
// this doesn't return anything neither
AContext.Connection.IOHandler.ReadBytes(buf, -1, False);
l := length(buf);
if l > 0 then
Display('CLIENT', '(Bytes =' + IntToStr(l));
end;
我收到的数据是 "array of bytes",其中没有 ETX(或包尾),它的工作方式是这样的
- 一个客户端连接(开始一个 TCP 会话)
- Client发送N个字节的包
- 客户端断开连接(TCP 会话结束)
该包在前 8 个字节中包含一个包头和一个数据部分的长度。
我来自 TTCPServer 的旧代码如下所示:
procedure TDispatchScanThread.TCPServerOnAccept(Sender: TObject; ClientSocket: TCustomIpClient);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
begin
try
LogQueue.AddToLog(format('TCPServer receiving (onAccept)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
l := ClientSocket.PeekBuf(Buf, 8);
if (l <> SOCKET_ERROR) and (l = 8) then
begin
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if not(s = sHeaderID) then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
ClientSocket.ReceiveBuf(Buf[0], l + HEADER_SIZE); // total length is header + data
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
end else
begin
LogQueue.AddError(format('TCPServer socket error: %d',[l]));
end;
except
on E: Exception do
begin
LogQueue.AddError(format('TcpServerAccept Error: %s',[E.Message]));
FreeAndNil(ADispatchPacket);
end;
end; // except
end;
您收到的消息有一个结构 - 一个 4 字节的消息 ID,后跟一个指定消息数据长度的 4 字节的 ASCII 字符串,然后是实际的消息数据。
您的 TTCPServer
代码遵循该结构,但您的 TIdTCPServer
代码不是(甚至不接近!)。如果有的话,Indy 使这种工作变得容易,因为 Indy 的 TIdIOHandler
class 有许多方法可用于读取各种数据格式,而 TTCPServer
根本没有提供任何帮助你,您必须手动阅读和解析所有内容。
在这种情况下,您可以使用 TIdIOHandler.ReadString()
和 TIdIOHandler.ReadBytes()
方法,例如:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: string;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
IdBuf: TIdBytes;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
// check if IVD version header is valid for us
s := AContext.Connection.IOHandler.ReadString(4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
Move(ShortString(s)[1], Buf[0], 4);
s := AContext.Connection.IOHandler.ReadString(4);
l := StrToInt(s);
Move(ShortString(s)[1], Buf[4], 4);
AContext.Connection.IOHandler.ReadBytes(IdBuf, l);
Move(PByte(IdBuf)^, Buf[8], l);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
AContext.Connection.Disconnect;
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
或者,您可以使用 TIdIOHandler.ReadStream()
方法,例如:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
ABufStream : TIdMemoryBufferStream;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
try
AContext.Connection.IOHandler.ReadStream(ABufStream, 8, False);
// check if IVD version header is valid for us
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
AContext.Connection.IOHandler.ReadStream(ABufStream, l, False);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
finally
ABufStream.Free;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
AContext.Connection.Disconnect;
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
或者:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
ABufStream : TIdMemoryBufferStream;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
try
AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);
if ABufStream.Size < 8 then
raise Exception.CreateFmt('Invalid dispatch packet Header size %d', [ABufStream.Size]);
// check if IVD version header is valid for us
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
if ABufStream.Size < (l + HEADER_SIZE) then
raise Exception.CreateFmt('Invalid dispatch packet size %d, expected %d', [ABufStream.Size, l + HEADER_SIZE]);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
finally
ABufStream.Free;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
我正在尝试从 Delphi 的 TTCP 服务器迁移到 Indy 的 TidTCPServer (Delphi XE10.2),但我不知道如何在执行中读取传入数据,所有示例我发现正在使用需要 "ETX" 字符的 readln。而且我还没有找到接收到的字节数或任何长度。
那么我该如何阅读完整的包裹呢? 我想象的是这样的; 读取字节直到 "disconnect"
我在下面尝试了这个(尽管不是同时),我没有得到任何包或字节,除非我使用 telnet 客户端并从键盘输入字符 - 这将逐字节给我数据.但是来自“第 3 方客户端”的包裹从未出现。我看到客户端再次连接并断开连接。
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
Port : Integer;
PeerPort : Integer;
PeerIP : string;
msgFromClient : string;
msgToClient : string;
buf : TidBytes;
l :integer;
ABufStream : TMemoryStream;
begin
// this doesn't return anything
ABufStream := TMemoryStream.Create;
try
// AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);
AContext.Connection.IOHandler.InputBufferToStream(ABufStream, -1);
ABufStream.Position := 0;
ABufStream.WriteBuffer(buf, ABufStream.Size);
msgFromClient := format('received %d bytes',[ABufStream.Size]);
finally
ABufStream.Free;
end;
// this doesn't return anything neither
AContext.Connection.IOHandler.ReadBytes(buf, -1, False);
l := length(buf);
if l > 0 then
Display('CLIENT', '(Bytes =' + IntToStr(l));
end;
我收到的数据是 "array of bytes",其中没有 ETX(或包尾),它的工作方式是这样的
- 一个客户端连接(开始一个 TCP 会话)
- Client发送N个字节的包
- 客户端断开连接(TCP 会话结束)
该包在前 8 个字节中包含一个包头和一个数据部分的长度。
我来自 TTCPServer 的旧代码如下所示:
procedure TDispatchScanThread.TCPServerOnAccept(Sender: TObject; ClientSocket: TCustomIpClient);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
begin
try
LogQueue.AddToLog(format('TCPServer receiving (onAccept)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
l := ClientSocket.PeekBuf(Buf, 8);
if (l <> SOCKET_ERROR) and (l = 8) then
begin
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if not(s = sHeaderID) then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
ClientSocket.ReceiveBuf(Buf[0], l + HEADER_SIZE); // total length is header + data
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
end else
begin
LogQueue.AddError(format('TCPServer socket error: %d',[l]));
end;
except
on E: Exception do
begin
LogQueue.AddError(format('TcpServerAccept Error: %s',[E.Message]));
FreeAndNil(ADispatchPacket);
end;
end; // except
end;
您收到的消息有一个结构 - 一个 4 字节的消息 ID,后跟一个指定消息数据长度的 4 字节的 ASCII 字符串,然后是实际的消息数据。
您的 TTCPServer
代码遵循该结构,但您的 TIdTCPServer
代码不是(甚至不接近!)。如果有的话,Indy 使这种工作变得容易,因为 Indy 的 TIdIOHandler
class 有许多方法可用于读取各种数据格式,而 TTCPServer
根本没有提供任何帮助你,您必须手动阅读和解析所有内容。
在这种情况下,您可以使用 TIdIOHandler.ReadString()
和 TIdIOHandler.ReadBytes()
方法,例如:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: string;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
IdBuf: TIdBytes;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
// check if IVD version header is valid for us
s := AContext.Connection.IOHandler.ReadString(4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
Move(ShortString(s)[1], Buf[0], 4);
s := AContext.Connection.IOHandler.ReadString(4);
l := StrToInt(s);
Move(ShortString(s)[1], Buf[4], 4);
AContext.Connection.IOHandler.ReadBytes(IdBuf, l);
Move(PByte(IdBuf)^, Buf[8], l);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
AContext.Connection.Disconnect;
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
或者,您可以使用 TIdIOHandler.ReadStream()
方法,例如:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
ABufStream : TIdMemoryBufferStream;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
try
AContext.Connection.IOHandler.ReadStream(ABufStream, 8, False);
// check if IVD version header is valid for us
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
AContext.Connection.IOHandler.ReadStream(ABufStream, l, False);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
finally
ABufStream.Free;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
AContext.Connection.Disconnect;
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;
或者:
procedure TForm2.IdTCPServerExecute(AContext: TIdContext);
var
s: ShortString;
l: integer;
Buf: TDataBuf; // TDataBuf = array[0..MAX_DATAPACKET] of byte;
ADispatchPacket: TDispatchPacket;
AQueuedStatus : Boolean;
ABufStream : TIdMemoryBufferStream;
begin
LogQueue.AddToLog(format('TCPServer receiving (onExecute)',[]), llvVerbose);
ZeroMemory(@Buf, MAX_DATAPACKET);
ABufStream := TIdMemoryBufferStream.Create(@Buf, SizeOf(Buf));
try
AContext.Connection.IOHandler.ReadStream(ABufStream, -1, True);
if ABufStream.Size < 8 then
raise Exception.CreateFmt('Invalid dispatch packet Header size %d', [ABufStream.Size]);
// check if IVD version header is valid for us
s := '0000'; // check if IVD version header is valid for us
Move(Buf[0], s[1], 4);
if s <> sHeaderID then
raise Exception.CreateFmt('Invalid dispatch packet HeaderID %s', [s]);
s := '0000'; // Fetch data package length
Move(Buf[4], s[1], 4);
l := StrToInt(s);
if ABufStream.Size < (l + HEADER_SIZE) then
raise Exception.CreateFmt('Invalid dispatch packet size %d, expected %d', [ABufStream.Size, l + HEADER_SIZE]);
// Create the dispatch packet object, move the data to the buffer and queue it.
ADispatchPacket := TDispatchPacket.Create;
try
ADispatchPacket.ReplyTime := MilliSecondOfTheDay(UTCNow);
ADispatchPacket.DataBuf := Buf; // the setter copies the buffer contens
AQueuedStatus := CommandQueue.Enqueue(TOmniMessage.Create(ord(itmDispatchPackage), ADispatchPacket));
except
ADispatchPacket.Free;
raise;
end;
finally
ABufStream.Free;
end;
LogQueue.AddToLog(Format('Received package: %d bytes, incoming package was queued = [%s]',[l + HEADER_SIZE, booltostr(AQueuedStatus, True)]), llvVerbose);
end;
procedure TForm2.IdTCPServerException(AContext: TIdContext; AException: Exception);
begin
LogQueue.AddError(format('TcpServer Error: %s',[AException.Message]));
end;