【问题标题】:Why doesn't TCusomWinSocket.ReceiveBuf ever return 0?为什么 TCusomWinSocket.ReceiveBuf 不返回 0?
【发布时间】:2012-01-05 19:46:36
【问题描述】:

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

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

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;

阅读器中的循环无休止地重复,我不知道为什么。问题的根源可能是什么?

【问题讨论】:

  • 如果我没记错你应该使用'直到 cnt
  • 同意。 ReceiveBuf 可能返回 -1 (SOCKET_ERROR)
  • ReceiveBuf() 仅在Count=-1 且没有可用数据(ReceiveLength() 内部使用)或套接字断开连接时返回 0。如果套接字在非阻塞套接字上报告WSAEWOULDBLOCK 错误,或者发生真正的套接字错误并且分配的OnError 事件处理程序设置ErrorCode := 0ReceiveBuf() 返回 -1。否则,ReceiveBuf() 将引发 ESocketError 异常。

标签: delphi sockets


【解决方案1】:

SendStream() 不是一个特别好的选择 - 永远。它旨在发送整个TStream,然后在完成后释放它。但是,如果套接字设置为非阻塞模式并且套接字在发送期间阻塞,SendStream() 将立即退出并且不会释放TStream。您必须再次致电SendStream() 才能继续从SendStream() 停止的位置发送TStream。但是还有其他情况会导致SendStream() 退出并释放TStream,而且你真的不知道它何时释放或没有释放TStream,因此尝试调用@987654331 变得非常危险@ 再次使用相同的 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^, 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;

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.Memory, Stream.Size);
end;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-27
    • 2015-06-21
    • 2022-01-23
    • 1970-01-01
    • 2011-03-01
    • 2011-07-01
    • 2011-01-17
    • 2018-04-05
    相关资源
    最近更新 更多