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