SendStream()
nie jest szczególnie dobrym wyborem do użycia - nigdy. Jest przeznaczony do wysłania całego urządzenia TStream
, a następnie zwolnienia po zakończeniu. Jeśli jednak gniazdo jest ustawione w trybie bez blokowania, a bloki gniazdka podczas wysyłania, SendStream()
natychmiast wychodzi i NIE ZAPISZ TStream
. Musisz ponownie zadzwonić pod numer SendStream()
, aby kontynuować wysyłanie TStream
z miejsca, w którym pozostawiono SendStream()
. Ale istnieją inne warunki, które mogą spowodować, że SendStream()
zakończy działanie i zwolni TStream
, a ty naprawdę nie wiesz kiedy to się stało lub nie zwolniłeś TStream
, więc bardzo niebezpieczne staje się wywołanie SendStream()
ponownie z tym samym obiektem TStream
. O wiele bezpieczniejszym sposobem jest uniknięcie za wszelką cenę numeru SendStream()
i zamiast tego zadzwonić pod numer SendBuf()
bezpośrednio do własnej pętli.
Po tym, nie informuje odbiorcy, ile bajtów zostanie wysłanych, więc odbiornik nie wie, kiedy przerwać czytanie (chyba że zamkniesz połączenie po wysłaniu TStream
). Lepszym wyborem jest wysłanie planowanej liczby bajtów przed wysłaniem danych TStream
. W ten sposób odbiornik może najpierw odczytać liczbę bajtów, a następnie zatrzymać czytanie, gdy zostanie odebrana określona liczba bajtów. Na przykład:
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;
jeśli się nie mylę należy użyć „aż CNT <1”, nie widzę w tej chwili co „ReceiveBuf” zwraca ... – ComputerSaysNo
Zgoda. ReceiveBuf może zwrócić -1 (SOCKET_ERROR) –
'ReceiveBuf()' zwraca 0 tylko wtedy, gdy 'Count = -1' i żadne dane nie są dostępne (używane wewnętrznie przez' ReceiveLength() ') lub gdy gniazdo jest odłączone. 'ReceiveBuf()' zwraca -1, jeśli gniazdo zgłasza błąd 'WSAEWOULDBLOCK' na nie blokującym gnieździe lub wystąpi prawdziwy błąd gniazda i przydzielona funkcja obsługi zdarzenia' OnError' ustawia kod błędu: Error = 0. W przeciwnym razie 'ReceiveBuf()' spowoduje zgłoszenie wyjątku 'ESocketError'. –