delphi – 为什么TCusomWinSocket.ReceiveBuf没有返回0?

说到套接字,TClientSocket和TServerSockets是我最喜欢的,因为它们使用简单.

我的任务很简单.我需要通过这两个组件发送一个文件(RAW),所以我有两个例程,如下所示:

procedure csRead(Sender: TObject; Socket: TCustomWinSocket);
var
   MSCli : TMemoryStream;
   cnt   : Integer;
   buf   : array [0..1023] of byte;
begin
    MSCli := TMemoryStream.Create;
    try
    repeat
      cnt := Socket.ReceiveBuf(buf[0],1024); //This loop repeats endlesly
      MSCli.Write(buf[0],cnt)
    until cnt = 0;
    finally
    MSCli.SaveToFile('somefile.dmp');
    MSCli.Free;
    end;
end;

当然发件人:

//...some code
    MSSErv.LoadFromFile('some file');
    MSServ.Position := 0;
    Socket.SendStream(MSServ);
  end;

读者的循环是不知不觉地重复,我不知道为什么.可能是问题的根源是什么?

解决方法

SendStream()不是一个特别好的选择 – 永远.它旨在发送整个TStream,然后在完成后释放它.但是,如果套接字设置为非阻塞模式并且套接字在发送期间阻塞,则SendStream()立即退出并且不释放TStream.您必须再次调用SendStream()以继续从SendStream()停止的位置发送TStream.但是还有其他条件会导致SendStream()退出并释放TStream,并且你真的不知道它何时释放或者没有释放TStream,因此尝试再次调用SendStream()变得非常危险.相同的TStream对象.一种更安全的方法是不惜一切代价避免使用SendStream(),而是直接在自己的循环中调用SendBuf().

话虽如此,SendStream()不会通知接收器将发送多少字节,因此接收器不知道何时停止读取(除非您在发送TStream后关闭连接).更好的选择是在发送TStream数据之前发送预期的字节数.这样,接收器可以先读取字节数,然后在收到指定的字节数时停止读取.例如:

procedure ReadRawFromSocket(Socket: TCustomWinSocket; Buffer: Pointer; BufSize: Integer);
var 
  buf: PByte; 
  cnt: Integer; 
begin 
  buf := PByte(Buffer); 
  while BufSize > 0 do
  begin 
    cnt := Socket.ReceiveBuf(buf^,BufSize);
    if cnt < 1 then begin
      if (cnt = -1) and (WSAGetLastError() = WSAEWOULDBLOCK) then
      begin
        Application.ProcessMessages;
        Continue;
      end;
      Abort;
    end;
    Inc(buf,cnt);
    Dec(BufSize,cnt);
  end; 
end;

function ReadInt64FromSocket(Socket: TCustomWinSocket): Int64;
begin
  ReadRawFromSocket(Socket,@Result,SizeOf(Int64));
end;

procedure ReadMemStreamFromSocket(Socket: TCustomWinSocket: Stream: TMemoryStream);
var
  cnt: Int64; 
begin
  cnt := ReadInt64FromSocket(Socket);
  if cnt > 0 then
  begin
    Stream.Size := cnt; 
    ReadRawFromSocket(Socket,Stream.Memory,cnt);
  end; 
end;

procedure csRead(Sender: TObject; Socket: TCustomWinSocket); 
var 
  MSCli : TMemoryStream; 
begin 
  MSCli := TMemoryStream.Create; 
  try 
    ReadMemStreamFromSocket(Socket,MSCli);
    MSCli.SaveToFile('somefile.dmp'); 
  finally
    MSCli.Free; 
  end; 
end; 

procedure SendRawToSocket(Socket: TCustomWinSocket; Buffer: Pointer; BufSize: Integer);
var 
  buf: PByte; 
  cnt: Integer; 
begin 
  buf := PByte(Buffer); 
  while BufSize > 0 do
  begin 
    cnt := Socket.SendBuf(buf^,cnt);
  end; 
end;

procedure SendInt64ToSocket(Socket: TCustomWinSocket; Value: Int64);
begin
  SendRawToSocket(Socket,@Value,SizeOf(Int64));
end;

procedure SendMemStreamToSocket(Socket: TCustomWinSocket: Stream: TMemoryStream);
begin
  SendInt64FromSocket(Socket,Stream.Size);
  SendRawToSocket(Socket,Stream.Size);
end;

begin
  ...
  MSSErv.LoadFromFile('some file'); 
  MSServ.Position := 0; 
  SendMemStreamToSocket(Socket,MSServ);
  ...
end;

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...