Reputation: 41
When I call TTcpClient.SendStream(MyStream), it advances MyStream.Position to 8704, and I have to call SendStream() repeatedly to get it to completely send my stream. However, the data received is missing chunks of 512 bytes about every 8K.
Note: This question is rhetorical, because I suffered through trying and failing to find a solution on the web. I found the bug in Delphi 7 Sockets.pas, and want to publish the solution for the good of the community.
Upvotes: 1
Views: 126
Reputation: 41
The problem is a coding bug in Delphi 7 Sockets.pas. The bug causes any stream larger than about 8K (exact size is OS-dependent) to lose 512-byte chunks of data. The SendStream implementation uses a repeat..until loop to pull 512-byte buffers from the caller's stream for sending with SendBuf(), and it continues as long as the stream has data and SendBuf() does not return equal to SOCKET_ERROR. The loss occurs when the Windows socket buffer fills, causing SendBuf() to return equal to SOCKET_ERROR, but at that point up to 512 bytes have already been read from the caller's stream and the stream Position has been advanced - but that Position is not restored on exit. Original Sockets.pas code:
function TBaseSocket.SendStream(AStream: TStream): Integer; var BufLen : Integer; Buffer: array[0..511] of Byte; begin Result := 0; if Assigned(AStream) then begin repeat BufLen := AStream.Read(Buffer, SizeOf(Buffer)); until (BufLen = 0) or (SendBuf(Buffer, BufLen) = SOCKET_ERROR); end; end;
And here's a fix:
function TBaseSocket.SendStream(AStream: TStream): Integer; var Quit : boolean; BufLen,OldPosition : Integer; Buffer: array[0..511] of Byte; begin Result := 0; if Assigned(AStream) then begin repeat OldPosition := AStream.Position; BufLen := AStream.Read(Buffer, SizeOf(Buffer)); if (BufLen > 0) then begin Quit := (SendBuf(Buffer, BufLen) = SOCKET_ERROR); if Quit then AStream.Position := OldPosition; //restore! end else begin //BufLen = 0 Quit := true; end; until Quit; end; end;
Upvotes: 2