I am using Delphi 5 with TServerSocket and TClientSocket. I use
socket.sendtext() to send small text messages successfully. When I have to
send larger text messages I am not sure that it reaches the destination in
one packet. Is there any minimum packet size that I can be sure will be
received at the destination in a single Read event.
Regards
Allan
Yes - one byte <g>
Sorry, but that's just about it. TCP transfers 'octet streams'. If you
want to send structured messages with TCP, rather than a stream of
bytes, you have to add a protocol on top to 'block up' the bytes into
your application protocol-unit.
Any other approach, eg. sending very small messages and hoping that they
will always be received in one read, will screw up, sooner or later.
'Messages' will get split and/or concatenated, at the will of the network.
Rgds,
Martin
> I am using Delphi 5 with TServerSocket and TClientSocket. I
> use socket.sendtext() to send small text messages successfully.
SendText() is not very reliable. It is not guaranteed to send the whole
text. It will return the number of bytes actually sent. If the size is
less then the string length, you would have to call SendText() again to
re-send the portions that were not sent earlier. In which case, you should
use SendBuf() directly instead of SendText().
You also have to take into account that if you use the components in
non-blocking mode, if SendText() (or SendBuf()) returns -1 then you have to
wait for the OnWrite event to be triggered on the connection before you can
send any more data to it. If you have data that is pending during that
time, you will have to cache it, and then send your cache when the event is
triggered.
> When I have to send larger text messages I am not sure that it
> reaches the destination in one packet.
There is no guarantee of that, even with smaller text values.
It is the sender's responsibility to format the data in such a way that the
receiver can detect where one value ends and the next begins, either by
sending each values actual size with the data, or by placing a unique byte
sequence in between each value. This is important in situations where a
single value is split amongst multiple packets, and when multiple values are
merged into a single packet. TCP/IP is a byte stream. There are no
guarantees about the contents of individual packets. The contents of
logical values that are made up by the packets have to be managed in your
own code.
It is the receiver's responsibility to cache all inbound packets, and to
only process the data values when they are complete.
Go to http://www.deja.com and search through the newsgroup archives. These
topics have been discussed in more detail many times before.
> Is there any minimum packet size that I can be sure will be received at
> the destination in a single Read event.
No.
Gambit
I was checking out Sendbuf() with non-blocking sockets. Did not know how to
trigger OnWrite. Checked the Internet and found a crude method, but it
works. coming back to my earlier question, will this method assure me of at
least 1Kb of transfer per receive at the Server's end.
procedure TForm1.ButtonSendDataClick(Sender: TObject);
begin
wTransportBuffer:='abcdefghijklmnop.....' ; // Just some text matter less
than 1kb
// Just trigerring OnWrite
ClientSocket1.Socket.ASyncStyles:=ClientSocket1.Socket.ASyncStyles-[asWrite]
;
ClientSocket1.Socket.ASyncStyles:=ClientSocket1.Socket.ASyncStyles+[asWrite]
;
end;
procedure TForm1.ClientSocket1Write(Sender: TObject; Socket:
TCustomWinSocket);
begin
if wTransportBuffer <>'' then
begin
ClientSocket1.Socket.Sendbuf(wTransportBuffer,length(wTransportBuffer))
;
wTransportBuffer:='' ;
end;
end;
Thanks for being around
Allan
Remy Lebeau (TeamB) <no....@no.spam.com> wrote in message
news:4523effc$1...@newsgroups.borland.com...
> I was checking out Sendbuf() with non-blocking sockets. Did not
> know how to trigger OnWrite.
You do not trigger it yourself. It is triggered automatically by the socket
when needed, just like the OnRead event is triggered automatically.
> Checked the Internet and found a crude method, but it works.
That is the completely wrong thing to do. You are not using the socket
correctly now.
> coming back to my earlier question, will this method assure me of
> at least 1Kb of transfer per receive at the Server's end.
No. Nothing you can do can guarantee that. Your reads must take into
account the actual size of each received packet. You must cache the
incoming packets in memory, and then process the cached data as needed when
complete blocks are available. Which means you must format your data in
such a way that you can differentiate between separate blocks. For example:
type
TSocketBuffer = class(TMemoryStream)
public
procedure Compact;
function Expand(ASize: Integer): Pointer;
procedure Flush(ASocket: TCustomWinSocket);
function Send(ASocket: TCustomWinSocket; const ABuffer; ASize:
Integer): Boolean;
function ReadString(var VStr: String): Boolean;
function SendString(ASocket: TCustomWinSocket; const AStr:
String): Boolean;
end;
procedure TSocketBuffer.Compact;
var
I: Integer;
P: PByte;
begin
Position > 0 then
begin
I := Size - Position;
if I > 0 then
begin
P := PByte(Memory);
Move(P[Position], Memory, I);
Size := I;
end else
Clear;
end;
end;
function TSocketBuffer.Expand(ASize: Integer): Pointer;
var
Old: Integer;
P: PByte;
begin
Old := Size;
Size := (Old + ASize);
P := PByte(Memory);
Result := @P[Old];
end;
procedure TSocketBuffer.Flush(ASocket: TCustomWinSocket);
var
P: PByte;
I: Integer;
begin
Position := 0;
P := PByte(Memory);
while Position < Size do
begin
I := ASocket.SendBuf(P[Position], Size - Position);
if I < 1 then Break;
Seek(I, soFromCurrent);
end;
Compact;
end;
function TSocketBuffer.Send(ASocket: TCustomWinSocket; const ABuffer;
ASize: Integer): Boolean;
var
P: PByte;
I: Integer;
begin
Result := False;
P := PByte(ABuffer);
if Size = 0 then
begin
while ASize > 0 do
begin
I := ASocket.SendBuf(P, ASize);
if I < 0 then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then Exit;
Break;
end;
if I = 0 then Exit; // disconnected
Inc(P, I);
Dec(ASize, I);
end;
end;
if ASize > 0 then
begin
Seek(0, soFromEnd);
Write(P, ASize);
end;
Result := True;
end;
function TSocketBuffer.ReadString(var VStr: String): Boolean;
var
I :Integer;
begin
Result := False;
VStr := '';
if (Size - Position) >= SizeOf(Integer) then
begin
Read(@I, SizeOf(I));
if (Size - Position) >= I then
begin
if I > 0 then
begin
SetLength(VStr, I);
Read(VStr[1], I);
end;
Result := True;
end else
Seek(-SizeOf(Integer), soFromCurrent);
end;
end;
function TSocketBuffer.SendString(ASocket: TCustomWinSocket; const AStr:
String): Boolean;
var
I: Integer;
begin
I := Length(AStr);
Result := Send(ASocket, @I, SizeOf(Integer));
if Result then
Result := SendBuffer(ASocket, PChar(AStr), I);
end;
--- client ---
var
OutboundBuffer: TSocketBuffer;
constructor TForm1.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OutboundBuffer := TSocketBuffer.Create;
end;
destructor TForm1.Destroy;
begin
OutboundBuffer.Free;
inherited Destroy;
end;
procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket:
TCustomWinSocket);
begin
OutboundBuffer.Clear;
end;
procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket:
TCustomWinSocket);
begin
OutboundBuffer.Clear;
end;
procedure TForm1.ClientSocket1Write(Sender: TObject; Socket:
TCustomWinSocket);
begin
OutboundBuffer.Flush(Socket);
end;
procedure TForm1.ButtonSendDataClick(Sender: TObject);
begin
TSocketBuffer.SendString(ClientSocket1.Socket,
'abcdefghijklmnop.....');
end;
--- sender ---
procedure TForm1.ServerSocket1Connect(Sender: TObject; Socket:
TCustomWinSocket);
begin
Socket.Data := TSocketBuffer.Create;
end;
procedure TForm1.ServerSocket1Disconnect(Sender: TObject; Socket:
TCustomWinSocket);
begin
TSocketBuffer(Socket.Data).Free;
Socket.Data := nil;
end;
procedure TForm1.ServerSocket1Read(Sender: TObject; Socket:
TCustomWinSocket);
var
InboundBuffer: TSocketBuffer;
I, Old: Integer;
S: String;
begin
InboundBuffer := TSocketBuffer(Socket.Data);
I := Socket.ReceiveLength;
if I < 1 then Exit;
Old := InboundBuffer.Size;
P := InboundBuffer.Expand(I);
I := Socket.ReceiveBuf(P, I);
if I < 0 then
begin
InboundBuffer.Size := Old;
Exit;
end;
InboundBuffer.Size := (Old + I);
InboundBuffer.Position := 0;
while InboundBuffer.ReadString(S) do
// use S as needed ...
InboundBuffer.Compact;
end;
Gambit