说到套接字,TClientSocket和TServerSockets是我最喜欢的,因为它们使用简单。
我的任务很简单。我需要通过这两个组件发送一个文件(RAW),所以我有两个例程,如下所示:
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;
读者的循环在不知不觉中重复,我不知道为什么。可能是问题的根源是什么?
答案 0 :(得分:5)
SendStream()
不是一个特别好的选择 - 永远。它旨在发送整个TStream
,然后在完成后释放它。但是,如果套接字设置为非阻塞模式且套接字在发送期间阻塞,SendStream()
会立即退出,并且不会释放TStream
。您必须再次致电SendStream()
以继续从TStream
停止的位置发送SendStream()
。但是还有其他条件会导致SendStream()
退出并释放TStream
,而您实际上并不知道它何时释放或未释放TStream
,因此它变得非常危险尝试使用相同的SendStream()
对象再次呼叫TStream
。一种更安全的方法是不惜一切代价避免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;