Multithreading 在线程内的套接字上接收文件时出错

Multithreading 在线程内的套接字上接收文件时出错,multithreading,sockets,delphi,Multithreading,Sockets,Delphi,我无法接收包含PNG文件的字节数组。 当代码在OnClientRead事件中执行时,它工作正常,在为线程传输代码时,会发生MemoryStream错误,该错误表示: 扩展内存流时内存不足 在这一点上: 如果SD.State=ReadingSize,则 我想知道如何解决此特定问题,以及如何检查接收到的数据是否包含文件或简单的字符串 守则: type TSock_Thread = class(TThread) private Socket: TCustomWinSocket; p

我无法接收包含PNG文件的字节数组。 当代码在
OnClientRead
事件中执行时,它工作正常,在为线程传输代码时,会发生
MemoryStream
错误,该错误表示:

扩展内存流时内存不足

在这一点上:

如果SD.State=ReadingSize,则

我想知道如何解决此特定问题,以及如何检查接收到的数据是否包含文件或简单的
字符串

守则:

type
  TSock_Thread = class(TThread)
  private
    Socket: TCustomWinSocket;
  public
    constructor Create(aSocket: TCustomWinSocket);
    procedure Execute; override;
  end;
  
type
  TInt32Bytes = record
    case Integer of
      0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
      1: (Value: Int32);
  end;

  TSocketState = (ReadingSize, ReadingStream);

  TSocketData = class
  public
    Stream: TMemoryStream;
    Png: TPngImage;
    State: TSocketState;
    Size: TInt32Bytes;
    Offset: Integer;
    constructor Create;
    destructor Destroy; override;
  end;
  
 { ... }

constructor TSock_Thread.Create(aSocket: TCustomWinSocket);
begin
  inherited Create(true);
  Socket := aSocket;
  FreeOnTerminate := true;
end;

procedure TSock_Thread.Execute;
var
  s: String;
  BytesReceived: Integer;
  BufferPtr: PByte;
  SD: TSocketData;
  Item: TListItem;
begin
  inherited;

  while Socket.Connected do
  begin
    if Socket.ReceiveLength > 0 then
    begin
      s := Socket.ReceiveText;

     { SD := TSocketData(Socket.Data);

      if SD.State = ReadingSize then
      begin
        while SD.Offset < SizeOf(Int32) do
        begin
          BytesReceived := Socket.ReceiveBuf(SD.Size.Bytes[SD.Offset],
            SizeOf(Int32) - SD.Offset);
          if BytesReceived <= 0 then
            Exit;
          Inc(SD.Offset, BytesReceived);
        end;
        SD.Size.Value := ntohl(SD.Size.Value);
        SD.State := ReadingStream;
        SD.Offset := 0;
        SD.Stream.Size := SD.Size.Value;
      end;

      if SD.State = ReadingStream then
      begin
        if SD.Offset < SD.Size.Value then
        begin
          BufferPtr := PByte(SD.Stream.Memory);
          Inc(BufferPtr, SD.Offset);
          repeat
            BytesReceived := Socket.ReceiveBuf(BufferPtr^,
              SD.Size.Value - SD.Offset);
            if BytesReceived <= 0 then
              Exit;
            Inc(BufferPtr, BytesReceived);
            Inc(SD.Offset, BytesReceived);
          until SD.Offset = SD.Size.Value;
        end;
        try
          SD.Stream.Position := 0;
          SD.Png.LoadFromStream(SD.Stream);
          SD.Stream.Clear;
        except
          SD.Png.Assign(nil);
        end;
        Item := Form1.ListView1.Selected;
        if (Item <> nil) and (Item.Data = Socket) then
          Form1.img1.Picture.Graphic := SD.Png;
        SD.State := ReadingSize;
        SD.Offset := 0;
      end;  }

    end;
    Sleep(100);
  end;

end;

procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
var
  TST: TSock_Thread;
begin
  TST := TSock_Thread.Create(Socket);
  TST.Resume;
end;
此代码在此行有数据转换错误:
DataSize:=stroint(sl)

我怎样才能解决这个问题

如何解决这一具体问题

您没有按照预期的方式使用
TServerSocket
线程

如果要在
stThreadBlocking
模式下使用
TServerSocket
(请参阅如何在
stNonBlocking
模式下使用
TServerSocket
),正确的方法是:

  • 从中派生线程类
  • 重写其虚拟方法以执行I/O工作(通过)
  • 使用事件来实例化线程
如果不这样做,
TServerSocket
将创建自己的默认线程(在主线程中触发
OnClient(Read | Write)
事件),这将干扰手动线程

而且,你不需要我给你展示的状态机。这是针对事件驱动代码的。线程化I/O代码可以改为线性编写

尝试类似以下内容:

type
  TSock_Thread = class(TServerClientThread)
  private
    Png: TPngImage;
    procedure PngReceived;
  protected
    procedure ClientExecute; override;
  end;

type
  TInt32Bytes = record
    case Integer of
      0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
      1: (Value: Int32);
  end;

procedure TSock_Thread.ClientExecute;
var
  SocketStrm: TWinSocketStream;
  Buffer: TMemoryStream;
  Size: TInt32Bytes;
  Offset: Integer;
  BytesReceived: Integer;
  BufferPtr: PByte;
begin
  SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
  try
    Buffer := TMemoryStream.Create;
    try
      Png := TPngImage.Create;
      try
        while ClientSocket.Connected do
        begin
          if not SocketStrm.WaitForData(100) then Continue;

          Offset := 0;
          while Offset < SizeOf(Int32) do
          begin
            BytesReceived := SocketStrm.Read(Size.Bytes[Offset], SizeOf(Int32) - Offset);
            if BytesReceived <= 0 then Exit;
            Inc(Offset, BytesReceived);
          end;
          Size.Value := ntohl(Size.Value);
          Buffer.Size := Size.Value;
          BufferPtr := PByte(Buffer.Memory);

          Offset := 0;
          while Offset < Size.Value do
          begin
            BytesReceived := SocketStrm.Read(BufferPtr^, Size.Value - Offset);
            if BytesReceived <= 0 then Exit;
            Inc(BufferPtr, BytesReceived);
            Inc(Offset, BytesReceived);
          end;

          Buffer.Position := 0;
          try
            Png.LoadFromStream(Buffer);
          except
            Png.Assign(nil);
          end;

          Synchronize(PngReceived);
        end;
      finally
        Png.Free;
      end;
    finally
      Buffer.Free;
    end;
  finally
    SocketStrm.Free;
  end;
end;

procedure TSock_Thread.PngReceived;
var
  Item: TListItem;
begin
  Item := Form1.ListView1.Selected;
  if (Item <> nil) and (Item.Data = ClientSocket) then
    Form1.img1.Picture.Graphic := Png;
end;

procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
  SocketThread := TSock_Thread.Create(False, ClientSocket);
end;
类型
TSock_Thread=class(TServerClientThread)
私有的
Png:TPngImage;
程序设计;
受保护的
程序客户执行;推翻
结束;
类型
TInt32Bytes=记录
大小写整数
0:(字节:字节数组[0..SizeOf(Int32)-1];
1:(值:Int32);
结束;
程序TSock_Thread.ClientExecute;
变量
SocketStrm:TWinSocketStream;
缓冲区:TMemoryStream;
大小:32字节;
偏移量:整数;
接收字节数:整数;
BufferPtr:PByte;
开始
SocketStrm:=TWinSocketStream.Create(ClientSocket,5000);
尝试
缓冲区:=TMemoryStream.Create;
尝试
Png:=TPngImage.Create;
尝试
而ClientSocket.Connected.do
开始
如果不是SocketStrm.WaitForData(100),则继续;
偏移量:=0;
而偏移量如果BytesReceived
,则您可以根据需要根据数据类型处理数据。
然后基本上我让它向服务器发送一个
字符串
命令,并检查例如:
如果Pos({file}',Socket.ReceiveText)>0,则开始//处理数据结束;如果Pos({string},Socket.ReceiveText)>0,则开始//处理数据结束
我认为,
Socket
有一些属性来检查是否正在接收文件或字符串:-)。@JeffersonFarias不使用
ReceiveText()
。它没有消息边界的概念。由于所使用的协议本质上是二进制的,只需发送一个包含该类型的额外字节,例如:
。如果
类型
是字符串,
数据
是字节编码字符,如UTF-8。否则,
数据
是一个文件/PNG。等等,
OnGetThread
对我来说很好,当
ServerType=stThreadBlocking
@JeffersonFarias我研究VCL的内部工作原理已经将近20年了。我在许多项目中都使用了
TServerSocket
TClientSocket
,这两种模式都是如此。你以为你比我知道得多,那就自己去想吧。我已经给出了两种模式下的完整解决方案。你显然不知道自己在做什么,而选择忽略我。杰弗森法里亚斯更仔细地阅读了我的答案和之前的评论。我已经解决了这个问题。您不能使用自己的
TThread
类!时期别再要求了。在非阻塞模式下,
tserverssocket
不使用自己的任何线程,而且您也不打算使用自己的线程,所以不要尝试。在阻塞模式下,
TServerSocket
使用自己的线程来接受连接,然后在连接客户机后,必须对I/O逻辑使用
TServerClientThread
。任何其他内容都超出了ServerSocket的设计范围。现在请注意告诉你的事情。
type
  TSock_Thread = class(TServerClientThread)
  private
    Png: TPngImage;
    procedure PngReceived;
  protected
    procedure ClientExecute; override;
  end;

type
  TInt32Bytes = record
    case Integer of
      0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
      1: (Value: Int32);
  end;

procedure TSock_Thread.ClientExecute;
var
  SocketStrm: TWinSocketStream;
  Buffer: TMemoryStream;
  Size: TInt32Bytes;
  Offset: Integer;
  BytesReceived: Integer;
  BufferPtr: PByte;
begin
  SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
  try
    Buffer := TMemoryStream.Create;
    try
      Png := TPngImage.Create;
      try
        while ClientSocket.Connected do
        begin
          if not SocketStrm.WaitForData(100) then Continue;

          Offset := 0;
          while Offset < SizeOf(Int32) do
          begin
            BytesReceived := SocketStrm.Read(Size.Bytes[Offset], SizeOf(Int32) - Offset);
            if BytesReceived <= 0 then Exit;
            Inc(Offset, BytesReceived);
          end;
          Size.Value := ntohl(Size.Value);
          Buffer.Size := Size.Value;
          BufferPtr := PByte(Buffer.Memory);

          Offset := 0;
          while Offset < Size.Value do
          begin
            BytesReceived := SocketStrm.Read(BufferPtr^, Size.Value - Offset);
            if BytesReceived <= 0 then Exit;
            Inc(BufferPtr, BytesReceived);
            Inc(Offset, BytesReceived);
          end;

          Buffer.Position := 0;
          try
            Png.LoadFromStream(Buffer);
          except
            Png.Assign(nil);
          end;

          Synchronize(PngReceived);
        end;
      finally
        Png.Free;
      end;
    finally
      Buffer.Free;
    end;
  finally
    SocketStrm.Free;
  end;
end;

procedure TSock_Thread.PngReceived;
var
  Item: TListItem;
begin
  Item := Form1.ListView1.Selected;
  if (Item <> nil) and (Item.Data = ClientSocket) then
    Form1.img1.Picture.Graphic := Png;
end;

procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
  SocketThread := TSock_Thread.Create(False, ClientSocket);
end;