Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: idtcpserver send multiable memorystream sending



Permlink Replies: 4 - Last Post: Dec 7, 2017 11:03 AM Last Post By: Remy Lebeau (Te...
madammar ellias

Posts: 111
Registered: 8/17/17
idtcpserver send multiable memorystream sending
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 5, 2017 4:42 PM
i have rewritten idtcpserver from scratch , i am trying to send some audio data from client to server but i got few problems.
if one client send audio as memorystream and the server start to send this stream to every one is connected by using SendMemorys the data the other writeln becomes very slow , if 2 or 3 clients send audio data as memory stream at the same time to all connected clients the memory stream send becomes very very slow From the server memorysize does not reach 300 its not really big to handle , what could be the problem ?

here is the full server code


unit serverunit;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer,
  System.SyncObjs, IdThreadSafe, IdYarn,
  IdTCPConnection, idGlobal, IdSocketHandle;
 
 
type
  TConnection = class(TIdServerContext)
  private
    WriteLock: TCriticalSection;
    procedure SendAudiomemory(MS : TMemorystream);
 
 
  public
    OutboundCache: TIdThreadSafeStringList;
    Name: String;
    IP: String;
    Connected: TDateTime;
    UniqueID: Dword;
    Allowed : string;
 
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
      AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;
 
    procedure SendMemorys(MS : TMemorystream);
 
 
  end;
 
type
  Tserverfm = class(TForm)
    TcpServer: TIdTCPServer;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure TcpServerConnect(AContext: TIdContext);
    procedure TcpServerExecute(AContext: TIdContext);
  private
    { Private declarations }
    LastUniqueID: Dword;
    procedure UpdateBindings;
  public
    { Public declarations }
  end;
 
const
dlm = '~';
 
var
  serverfm: Tserverfm;
 
implementation
 
{$R *.dfm}
 
 
procedure Tserverfm.TcpServerConnect(AContext: TIdContext);
var
  Connection: TConnection;
begin
  Connection := AContext as TConnection;
 
  Connection.IP := AContext.Binding.PeerIP;
  Connection.Connected := Now;
  Connection.UniqueID := LastUniqueID;
 
if Connection.UniqueID = LastUniqueID then
begin
Connection.UniqueID := LastUniqueID + 1;
end;
 
LastUniqueID := Connection.UniqueID;
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := True;
 
end;
 
procedure Tserverfm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
Command: String;
SCMD : string;
Sdata : string;
Cache, OutboundCmds: TStringList;
Strm: TStream;
I: integer;
parsecount, P: integer;
cmdparse: array [1 .. 200] of String;
begin
Connection := AContext as TConnection;
 
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
 
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
Strm := TStream(OutboundCmds.Objects[I]);
if Strm <> nil then
AContext.Connection.IOHandler.Write(Strm, 0, true)
else
AContext.Connection.IOHandler.Write(Int64(0)); // or Int32 ...
end;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
 
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Exit;
end;
end;
 
SCMD := AContext.Connection.IOHandler.ReadLn;
 
if SCMD = '' then
begin
AContext.Connection.Disconnect;
Exit;
end;
 
Strm := nil;
try
 
if SCMD = 'TEXT' then
begin
 
with AContext.Connection.IOHandler do
begin
//read stream to a string
Sdata := AContext.Connection.IOHandler.ReadString(ReadInt64);
end;
 
//parse stream to parameter
 
parsecount := 0;
 
//start parsing
while (Sdata <> '') and (parsecount < 200) do
begin
 
Inc(parsecount);
P := Pos(dlm, Sdata);
if P = 0 then
cmdparse[parsecount] := Sdata
else
begin
cmdparse[parsecount] := Copy(Sdata, 1, P - 1);
Delete(Sdata, 1, P + 1);
end;
end;
 
 
Command := cmdparse[1];
 
 
if Command = 'LGN' then
Begin
  connection.Allowed := cmdparse[2];
End;
 
end
 
else
 
if SCMD = 'MEMBUF' then
begin
Strm := TMemoryStream.Create;
AContext.Connection.IOHandler.ReadStream(Strm, -1, False);
Strm.Position := 0;
Connection.SendAudiomemory(TMemoryStream(Strm));
end
 
// other commands as needed ...
 
else
begin
with AContext.Connection.IOHandler do
Discard(ReadInt64); // or ReadInt32() ...
end;
 
finally
Strm.Free;
end;
 
 
end;
 
 
procedure Tserverfm.UpdateBindings;
var
  Binding: TIdSocketHandle;
begin
  TcpServer.DefaultPort := StrToInt(Edit1.Text);
  TcpServer.Bindings.Clear;
  Binding := TcpServer.Bindings.Add;
  Binding.IP := '0.0.0.0';
  Binding.Port := StrToInt(Edit1.Text);
end;
 
 
{ TConnection }
 
procedure TConnection.SendAudiomemory(MS: TMemorystream);
var
  List: TList; // or TIdContextList if using a modern Indy version
  I: integer;
  Connection: TConnection;
begin
 
 
 
List := Server.Contexts.LockList;
try
 
for I := 0 to List.Count - 1 do
begin
Connection := TConnection(List.Items[I]);
 
if (Connection <> self) then
begin
 
Connection.SendMemorys(MS);
 
end;
 
end; // loop end
 
 
finally
Server.Contexts.UnlockList;
end;
 
end;
 
 
constructor TConnection.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
  AList: TIdContextThreadList);
begin
  inherited;
  WriteLock := TCriticalSection.Create;
  OutboundCache := TIdThreadSafeStringList.Create;
 
end;
 
destructor TConnection.Destroy;
var
  Cache: TStringList;
  I: integer;
begin
 
  if OutboundCache <> nil then
  begin
    Cache := OutboundCache.Lock;
    try
      for I := 0 to Cache.Count - 1 do
        Cache.Objects[I].Free;
    finally
      OutboundCache.Unlock;
    end;
    OutboundCache.Free;
  end;
 
  FreeAndNil(WriteLock);
 
  inherited;
end;
 
 
procedure TConnection.SendMemorys(MS: TMemorystream);
var
  CopiedStream: TMemoryStream;
 
begin
 
  CopiedStream := TMemoryStream.Create;
 
  try
    CopiedStream.CopyFrom(MS, 0);
    CopiedStream.Position := 0;
    OutboundCache.AddObject('MEMBUF', CopiedStream);
  except
    CopiedStream.Free;
   // raise;
  end;
 
 
end;
 
 
procedure Tserverfm.Button1Click(Sender: TObject);
begin
UpdateBindings;
 
  TcpServer.Active := true;
  LastUniqueID := 100;
  TcpServer.ContextClass := TConnection;
end;
 
end.
 
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: idtcpserver send multiable memorystream sending
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 5, 2017 5:40 PM   in response to: madammar ellias in response to: madammar ellias
madammar ellias wrote:

i have rewritten idtcpserver from scratch

It doesn't look much different from your earlier code. It is not much
of a "rewrite"...

i am trying to send some audio data from client to server

TCP is not really a good idea for broadcasting streaming media. UDP
broadcasting or IP multicasting is way better, especially if you use a
media protocol on top of it, such as RTP.

if one client send audio as memorystream and the server start to send
this stream to every one is connected by using SendMemorys the data
the other writeln becomes very slow

There is only one Writeln() call in the code you have shown, and its
speed is not affected by the number of clients the audio data is being
sent to. Each client runs in its own independent thread, so sending
data to multiple clients will run in parallel instead of sequentially.

if 2 or 3 clients send audio data as memory stream at the same time to
all connected clients the memory stream send becomes very very slow

Not surprising, given that TCP is not designed for broadcasting. The
more clients you have connected, the longer it takes to copy and queue
the audio data for each client. The more clients you have, the longer
SendAudiomemory() takes to run, which in turn slows down the client
that sent the audio. And you are doing other things when you are not
sending audio packets, so your outbound queue itsn't exactly real-time
anyway.

BTW, you have a small logic bug in your Form code - you are setting the
server's ContextClass property after activating the server. You need
to set it beforehand, otherwise you have a window of oppurtunity where
clients could connect and use the default TServerContext class instead
of your custom TConnection class.

--
Remy Lebeau (TeamB)
madammar ellias

Posts: 111
Registered: 8/17/17
Re: idtcpserver send multiable memorystream sending
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 6, 2017 12:33 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
TCP is not really a good idea for broadcasting streaming media. UDP
broadcasting or IP multicasting is way better, especially if you use a
media protocol on top of it, such as RTP.
--
Remy Lebeau (TeamB)

ifi understand correct i should create udp client and server for the streaming ?
if yes is udp client class can be the same of tcp ?

what i mean is can i broadcast in udp to a specific client ? where client class value equal something ? i am starting to look at the udp now

Edited by: madammar ellias on Dec 6, 2017 1:31 PM
Markus Humm

Posts: 5,113
Registered: 11/9/03
Re: idtcpserver send multiable memorystream sending [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 7, 2017 10:38 AM   in response to: madammar ellias in response to: madammar ellias
Am 06.12.2017 um 22:31 schrieb madammar ellias:
Remy Lebeau (TeamB) wrote:
TCP is not really a good idea for broadcasting streaming media. UDP
broadcasting or IP multicasting is way better, especially if you use a
media protocol on top of it, such as RTP.
--
Remy Lebeau (TeamB)

ifi understand correct i should create udp client and server for the streaming ?
if yes is udp client class can be the same of tcp ?

what i mean is can i broadcast in udp to a specific client ? where client class value equal something ? i am starting to look at the udp now

Edited by: madammar ellias on Dec 6, 2017 1:31 PM

Hello,

TCP is connection oriented, UDP not. You can still send UDP packets to a
single IP/port, but what Remy is most likely after is, that you can send
UDP packets as broadcasts or as multicasts, so if 2 or more clients wath
the same stream you can optimize delivery by not consuming so much
bandwidth.

Another aspect is, that TCP automatically retransmits lost packets,
which might be unwanted as it costs time. UDP doesn't, this means the
video/audio might drop a frame, but wil not have any "catch up" type
effects.

Greetings

Markus
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: idtcpserver send multiable memorystream sending [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Dec 7, 2017 11:03 AM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:

what Remy is most likely after is, that you can send UDP packets as
broadcasts or as multicasts, so if 2 or more clients wath the same
stream you can optimize delivery by not consuming so much bandwidth.

Reducing bandwidth is one thing, but it also simplies the coding, too.
You can send 1 packet to 1 IP/port, and the network will handle
delivering that packet to every interested receiver for you.

If you send the packet to a subnet broadcast IP, the network will
deliver the packet to every receiver that is listening on that subnet.

If you send the packet to a multicast group IP, the network will
deliver the packet to every receiver that has subscribed to that
multicast group.

Another aspect is, that TCP automatically retransmits lost packets,
which might be unwanted as it costs time. UDP doesn't, this means the
video/audio might drop a frame, but wil not have any "catch up" type
effects.

Yes, losing packets occasionally is not really a bad thing when
streaming audio/video, they can stand to lose data and will resync
periodically.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02