When it comes to sockets, TClientSocket and TServerSockets are my favourite because of their simple usage.
My task is very simple. I need to send a file (RAW) through these 2 components, so I have 2 routines like the ones below:
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;
And of course the sender :
//...some code
MSSErv.LoadFromFile('some file');
MSServ.Position := 0;
Socket.SendStream(MSServ);
end;
The loop in the reader is repeating endelessly and I don't know why. What could be the source of the problem?
SendStream()
is not a particularly good choice to use - EVER. It is intended to send the entire TStream
and then free it when finished. However, if the socket is set to non-blocking mode and the socket blocks during sending, SendStream()
exits immediately and DOES NOT free the TStream
. You have to call SendStream()
again to continue sending the TStream
from where SendStream()
left off. But there are other conditions that can cause SendStream()
to exit AND free the TStream
, and you don't really know when it did or did not free the TStream
, so it becomes very dangerous to try to call SendStream()
again with the same TStream
object. A much safer approach is to avoid SendStream()
at all costs and call SendBuf()
directly in your own loop instead.
With that said, SendStream()
does not inform the receiver how many bytes will be sent, so the receiver does not know when to stop reading (unless you close the connection after sending the TStream
). A better choice is to send the intended byte count before then sending the TStream
data. That way, the receiver can read the byte count first, then stop reading when the specified number of bytes have been received. For example:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With