2012-01-05 152 views
5

當談到套接字時,TClientSocket和TServerSockets是我最喜歡的,因爲它們的簡單用法。爲什麼TCusomWinSocket.ReceiveBuf不會返回0?

我的任務很簡單。我需要通過這兩個組件發送一個文件(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; 

在讀者的循環是無盡的重複,我不知道爲什麼。什麼可能是問題的根源?

+3

,如果我沒有記錯,你應該使用「直到CNT <1」,我看不出現在是什麼「ReceiveBuf」返回...... – ComputerSaysNo 2012-01-05 20:34:16

+1

同意。 ReceiveBuf可能返回-1(SOCKET_ERROR) – 2012-01-05 21:00:48

+2

ReceiveBuf()僅在Count = -1時返回0,並且沒有數據可用(由內部的ReceiveLength()使用),或者套接字斷開連接。如果套接字在非阻塞套接字上報告WSAEWOULDBLOCK錯誤,或者發生實際套接字錯誤並且分配的OnError事件句柄設置ErrorCode:0,則ReceiveBuf()返回-1。否則,'ReceiveBuf()'會引發一個'ESocketError'異常。 – 2012-01-05 21:16:41

回答

5

SendStream()是不是一個特別好的選擇使用 - 永遠。它旨在發送整個TStream,然後在完成時釋放它。但是,如果套接字設置爲非阻塞模式並且在發送期間套接字塊被阻塞,則SendStream()會立即退出並且不釋放TStream。您必須再次致電SendStream()才能繼續發送從SendStream()中止的TStream。但是還有其他一些條件可能導致SendStream()退出並釋放TStream,並且您不知道它何時或沒有釋放TStream,因此嘗試再次使用同一個TStream對象調用SendStream()會變得非常危險。更安全的方法是不惜一切代價避免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;