2012-01-05 11 views
5

Jeśli chodzi o gniazda, TClientSocket i TServerSockets są moimi ulubionymi ze względu na ich proste użycie.Dlaczego TCusomWinSocket.ReceiveBuf nigdy nie zwraca 0?

Moje zadanie jest bardzo proste. Muszę wysłać plik (RAW) za pośrednictwem tych 2 składników, więc mam 2 rutyny, jak te poniżej:

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; 

i oczywiście nadawcy:

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

Pętla w czytniku jest powtarzanie bez końca i nie wiem dlaczego. Jakie może być źródło problemu?

+3

jeśli się nie mylę należy użyć „aż CNT <1”, nie widzę w tej chwili co „ReceiveBuf” zwraca ... – ComputerSaysNo

+1

Zgoda. ReceiveBuf może zwrócić -1 (SOCKET_ERROR) –

+2

'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'. –

Odpowiedz

5

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; 
Powiązane problemy