RTC Forums
November 23, 2024, 03:39:42 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1] 2
  Print  
Author Topic: TRtcMessengerServer enhancement request/suggestion.  (Read 22230 times)
Kas
RTC Expired
*
Posts: 17


« on: July 22, 2018, 10:56:19 AM »

I would love TRtcMessengerServer to support long request data .

TRtcMessgengerServer can add nice features to a TCP server-client application without the hassle of utilizing additional ports specially if this application is not written in Delphi, that application is already using port 443, what i did is after checking if the request is POST or GET i passed the requests/data to DLL that have this TRtcMessengerServer and it worked like magic, i can so lot of things remotely and even from a web browser, but the limit of 32000 bytes prevent me from adding more features or unleash its power like adding full web server to access log files from a browser or browsing big folders ( in case of Portal File Transfer ) or even use the portal RDP.

So would you please consider to enhance this powerful components ( server and client )? or show us how to do it ?


ps: Examples folder lack a sample for those components like the others, you always did it the right way, so it will be nice to see how it should be done .
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #1 on: July 22, 2018, 01:39:47 PM »

Even though "TRtcMessageClient" and "TRtcMessageServer" components use a 32KB buffer to read from the input stream, the number of bytes you can send and receive in a single request or response when using these components is NOT limited by the size of that buffer. The only thing the buffer size affects, is the number of times the "OnDataReceived" event will be triggered for requests and responses until the entire request or response content body was read. Since this is also the case when working with HTTP/s components (multiple "OnDataReceived" events will be triggered for requests and responses containing a larger content body), I fail to see the problem.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #2 on: July 22, 2018, 05:32:24 PM »

Then there is few things i am doing wrong , i don't have OnDataReceived in TRtcMessengerServer .

What i was doing something can be tested with this :

I dropped TRtcMessgerServer on a module and added TRtcPortalGateway , assigned the server in that Gateway , now on other server part (C++ TCP media relay server) ,i am receiving packets , then i am checking if those packets are HTTP then searching them for #13#10#13#10 after that i parse the content-length and make sure the received data is a complete http request ( header and content ) , after that i it using as aStream in ProcessData(aRequest, aReply: TStream) then sending the result stream aReply back if the size >0 , this works fine ! and the control can browse the remote files on the host and send and receive files, and i can browse folders it works and download files if those files are small relatively, and fail if the folder contains thousands files or files are big , same happen with show remove desktop , view desktop from the control will not work ( while desktop to host with small window around 100 pixel will work fine )

Should i drop this aReply stream and use RtcMsgServerDataOut instead ? How to access the raw response from Sender:TRtcConnection inside this event?
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #3 on: July 22, 2018, 07:54:42 PM »

TRtcMessageClient and TRtcMessageServer components were designed to be linked together for direct communication, or be used with 3rd-party transports designed for these components - like NexusDB transport plugins, which are included as a 3rd-party package, but require NexusDB components to compile. If you are trying to implement your own custom transport, you can check how TRtcMessageClient and TRtcMessageServer components work together when linked directly, or take a closer look at NexusDB plugins, but ... explaining how to correctly implement a custom transport for the TRtcMessageClient or the TRtcMessageServer component is beyond the scope of this Forum.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #4 on: July 22, 2018, 09:45:38 PM »

Thank you Danijel for answering and please forgive my English, may be i didn't clear about what i am asking for.

I am not asking for custom protocol but asking to enhance the portability and usability of RtcMessengerServer and RtcMessengerClient for that i posted in suggestion and features :

1_ I saw the NexusDB plugin and i managed to handle this limitation where i should pass one message and only one ( one full request with content ) to ProcessData , while i imagine you are planning to support HTTP2 where HTTP pipelining is needed , so adding such mechanism to feed ProcessData with bulk requests in those messenger components will be easier, where one can stream requests as whole or parts and those components will buffer and respond accordingly.

2_ Callback: in a case where gateway had received a request and a response should be sent to multiple connected client, it will be nice to have specific event triggered with Sender:RtcConnection for each connection intended to receive with the response content as raw .

3_ I still can't find the reason for why sometimes in my example using the portal i can send files bigger like 256k successfully and sometimes it fails at 33k, and my question is not about , and i gave an example where it fail to deliver on big request or responses , i am not sure yet ,
   A) if it is RtcPortal bug , then i know you stand about it .
   B) if it is a TRtcMessengerServer bug , then you have it .
   C) if it is by design , then please consider this a suggestion to enhance or modify , as i know you calling it messenger from a simple message .

The point is and was to use RTC with different application not the ones developed by Delphi or FreePascal.

Thank you Danijel again, wasting your time is not my intention.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #5 on: July 22, 2018, 11:22:40 PM »

Here is a simple way to see the limit with request :
New VCL project with RtcMessengerServer with the default TRtcPortalGateway copied from the portal gateway and a RtcTcpServer

Code:
procedure TForm6.RtcTcpServer1ClientConnect(Sender: TRtcConnection);
begin
  Sender.Info.asObj['con']:=RtcMessageServer1.GetConnection;
end;

procedure TForm6.RtcTcpServer1ClientDisconnect(Sender: TRtcConnection);
begin
  RtcMessageServer1.PutConnection(Sender.Info.asObj['con']);
  Sender.Info.asObj['con']:=nil;
end;

procedure TForm6.RtcTcpServer1DataReceived(Sender: TRtcConnection);
var
  B:RtcByteArray;
  aReq,aAns:TMemoryStream;
begin
  aReq:=TMemoryStream.Create;
  aAns:=TMemoryStream.Create;
  try
    B:=Sender.ReadEx;
    aReq.WriteBuffer(B[0],Length(B));
    aReq.Position:=0;
    TRtcMessageServer(Sender.Info.asObj['con']).ProcessData(aReq,aAns);
    if aAns.Size>0 then
    begin
      aAns.Position:=0;
      SetLength(B,aAns.Size);
      aAns.ReadBuffer(B[0],aAns.Size);
      Sender.WriteEx(B);
    end else
    begin
      /// this will be reached when the request is bigger than 32k
    end;
  finally
    aReq.Free;
    aAns.Free;
  end;
end;

The above will code will allow portal clients connect and browse folders and send files as long the files are small relatively and folder with not with so many files (few thousands) , in short this will fail ( no exception here but the aResponse stream is empty ) when a request content is bigger 32k, and this will break one response for one request.
and that what i meant for enhancing or removing the limitation.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #6 on: July 23, 2018, 03:25:50 PM »

As the name implies, TRtcMessageClient and TRtcMessageServer components are message-based client and server components. They were designed to process messages in a blocking way. A complete request is required to produce a complete response.

If you look at NexusDB transport plugins, you will see that their Server- and Client-side plugins ONLY use the "rtcTransports.pas" unit. The ONLY thing interesting in that unit is the "IRTCMessageReceiver" interface, which ONLY has one method: "ProcessMessage", as you can see below (copy/paste from the "rtcTransports.pas" unit) ...
 
type
  { @abstract(Transport Plugin interface)
    This interface should be *IMPLEMENTED* when writing a "receiver" transport plugin,
    which can be linked to TRtcMessageClient in place of the TRtcMessageServer component. }
  IRTCMessageReceiver = interface
    ['{C9E3606F-ED08-43F4-AF97-F55A3EC8F14B}']
    { ProcessMessage need to be called *only* once per request, with a complete request data
      waiting in the "aRequest" stream and an empty "aReply" stream to receive the response.
      Request processing is done in a "blocking mode", which means that the caller will
      have the complete response filled in the "aReply" stream, before the call returns. }
    procedure ProcessMessage(aRequest, aReply: TStream);
  end;

This is NOT a coincidence. This is BY DESIGN, to shield 3rd-party transport details from RTC component details and vice-versa, while allowing RTC components to communicate with each other through 3rd-party communication channels.

In other words, components linked to a TRtcMessageClient can communicate with components linked to TRtcMessageServer directly, or through a compatible pair of Client- and Server-transport plugins, without having to worry about the actual transport being used. At the same time, the transport plugin should NOT concern itself with the actual data being transported, nor should it make any assumptions about the data format used in requests and responses, but should handle each request and response as an opaque binary stream.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #7 on: July 23, 2018, 09:06:49 PM »

Thank you Danijel very much , that is helpful.

I couldn't find how to attach a file to this post so i will paste the code it may help someone, i just couldn't figure out how to disconnect RtcTcpServer and RtcMessengerServer ??:
Code:
type
  TForm6 = class(TForm)
    RtcMessageServer1: TRtcMessageServer;
    RtcTcpServer1: TRtcTcpServer;
    Gateway: TRtcPortalGateway;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure RtcTcpServer1Connect(Sender: TRtcConnection);
    procedure RtcTcpServer1DataReceived(Sender: TRtcConnection);
    procedure RtcTcpServer1Disconnect(Sender: TRtcConnection);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

procedure TForm6.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  RtcMessageServer1.StopListen;
  RtcTcpServer1.StopListen;
  RtcTcpServer1.Disconnect;
end;

procedure TForm6.FormCreate(Sender: TObject);
begin
  RtcTcpServer1.Listen;
end;

procedure TForm6.RtcTcpServer1Connect(Sender: TRtcConnection);
begin
  Sender.Info.asObj['con'] := RtcMessageServer1.GetConnection;
  Sender.Info.asObj['req'] := TMemoryStream.Create;
  Sender.Info.asObj['res'] := TMemoryStream.Create;
  Sender.Info.asInteger['reqsize'] := 0;
end;

procedure TForm6.RtcTcpServer1DataReceived(Sender: TRtcConnection);
const
  CONTENT_LEN = 'CONTENT-LENGTH:';
  CRLF = #13#10;

  function GetRequestSize(buf: RtcByteArray): Integer;
  var
    st, ss: RtcString;
    p, ct: Integer;
  begin
    Result := 0;
    p := PosEx(CRLF + CRLF, buf);
    if p > -1 then
    begin
      st:=Upper_Case(RtcBytesZeroToString(buf,0,p + 3));
      ct := PosEx(CONTENT_LEN, st);
      if ct > 0 then
        ss := RtcBytesZeroToString(buf, ct + Length(CONTENT_LEN), PosEx(#13#10,
          st, ct) - (ct + Length(CONTENT_LEN)) - 1)
      else
        Exit;
      Result := Str2IntDef(ss, 0) + Length(st);
    end;
  end;

var
  buf: RtcByteArray;
  aReq, aAns: TMemoryStream;
  rs: Integer;
begin
  aReq := TMemoryStream(Sender.Info.asObj['req']);
  aAns := TMemoryStream(Sender.Info.asObj['res']);
  buf := Sender.ReadEx;
  aReq.WriteBuffer(buf[0], Length(buf));

  rs := Sender.Info.asInteger['reqsize'];
  if rs = 0 then
  begin
    rs := GetRequestSize(buf);
    if rs = 0 then
    begin
      Sender.Disconnect;
      Exit;
    end;
    Sender.Info.asInteger['reqsize'] := rs;
  end;

  if rs <> aReq.Size then
  begin
    if rs < aReq.Size then
      Sender.Disconnect;
    Exit;
  end;

  aReq.Position := 0;
  TRtcMessageServer(Sender.Info.asObj['con']).ProcessData(aReq, aAns);
  if aAns.Size > 0 then
  begin
    aAns.Position := 0;
    SetLength(buf, aAns.Size);
    aAns.ReadBuffer(buf[0], aAns.Size);
    Sender.WriteEx(buf);
    aAns.Clear;
    aReq.Clear;
    Sender.Info.asInteger['reqsize'] := 0;
  end
  else
  begin
    Sender.Disconnect;
    Exit;
  end;

end;

procedure TForm6.RtcTcpServer1Disconnect(Sender: TRtcConnection);
begin
  RtcMessageServer1.PutConnection(Sender.Info.asObj['con']);
  Sender.Info.asObj['con'] := nil;
  TMemoryStream(Sender.Info.asObj['req']).Free;
  Sender.Info.asObj['req'] := nil;
  TMemoryStream(Sender.Info.asObj['res']).Free;
  Sender.Info.asObj['res'] := nil;
end;

end.

RtcTcpServer is must be multithreaded, i uninstalled Delphi IDE and EurekaLog and reinstalled them and the problem with big request seems to be resolved !
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #8 on: July 23, 2018, 09:09:20 PM »

Thanks for your complete example Project. I'm sure someone will benefit from it.

Anyway ... while you were working on this, I've released an update for the RTC SDK (v9.26), which might fix the problem with your previous (much simpler) example, because it should now be possible to use the "ProcessData" method to handle request content as it arrives, rather than storing it in a temporary buffer until the complete request content is here (as you did in your more complex example now), but ... please keep in mind that this is NOT how the components were meant to be used, so you might stumble over some other problems along the way, like - for example - memory issues when using an in-memory stream to accept the response (as you did in both of your examples) if you try to use this approach to download a large file, since the entire contents of the file would then be loaded into the memory stream before being sent out.

PS. I've moved this topic to the "Support" section now.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #9 on: July 23, 2018, 09:33:59 PM »

I was writing questions , and it is great support and understanding from you.

Thank you very much now it is working fine,  webserver and RtcPortal or the more important RtcRemoteFunction can be injected in any internet application easily, even adding web browser functionality, but i have few questions :
1_ i understand the consequences of using the memory like this but how to free the blocking thread ? (cancel/exit ProcessData )
2_ in my code above i couldn't figure out the right way to close and terminate , would you explain how to do it.
3_ If for some reason the thread that is blocking on ProcessData got terminated externally ( because it is not mine to start with ) how to recover ? if that is possible.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #10 on: July 23, 2018, 10:00:38 PM »

Sorry, but I don’t have answers to your latst questions. As said, you are using the components in a way in which they were NOT meant to be used, so there are no mechanisms in place to handle the extreme cases you have mentioned.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #11 on: July 23, 2018, 11:20:15 PM »

Third question that was worst case scenario, when things goes south, first two questions is what concern me more as i can't find working cancel or abort method.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #12 on: July 24, 2018, 08:54:07 AM »

Frankly, I think that you would be much better off by using the TRtcHttpServer component to host a fully asynchronous HTTP Server, which is designed for that purpose, instead of fiddling with the TRtcMessageServer and looking for workarounds to handle potential issues which could arise because of its blocking nature.

When your Client and your Server are running on the same machine (or in the same process), you can bind the Server to the ‘localhost’ address (assign it to the ‘ServerAddr’ property on the TRtcHttpServer component) to make it inaccessible from any other machine.

Also, by using ‘localhost’ as your destination address on the Client, you won’t be generating network traffic (all traffic to and from the ‘localhost’ address is handled internally by the socket API). You only need an available TCP/IP listening port, but with 64K port numbers to choose from, this shouldn’t be a problem.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #13 on: July 24, 2018, 09:15:44 AM »

I agree with you, but still it is neat Swiss Army Knife to have in the toolbox!
I need this for two application one written in Unity and the other is media server both are using TLS connection and strictly firewalled and the only way to achieve my goal fast and easy ( i am too lazy to waste time in that Unity ) without asking to change firewall settings, now i can support web browser without minimum modification in both servers, about the blocking i can make my own thread to block on ProcessData and release the caller thus i have non-blocking with simple one callback.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #14 on: July 25, 2018, 03:54:49 PM »

I found time to dig deeper and resolve the closing problem, i couldn't find a way other than this ugly hack.

I added WakeUpAllDelayedCall to rtcSrvModule and called this on termination, it worked fine and no more exceptions or delays, ProcessData exited as it should be, is there a way wake up specific calls based on the owner component itself ( RtcServerModule or RtcMessegerServer)

Code:
procedure WakeUpAllDelayedCall;
  var
    ob:TObject;
    i:RtcIntPtr;
  begin
  CS.Acquire;
  try
    if assigned(List) then
      begin
        i:=List.search_min(ob);
      while assigned(ob) do
        begin
          if ob is TRtcDelayedCall then
          TRtcDelayedCall(ob).WakeUp;
          i:=List.search_g(i, ob);
        end;
      end;
  finally
    CS.Release;
    end;
end;

Would you please add such last approach as solution for extreme cases ? ( it is ugly and wrong but might prevent freezing or crashing )

ps: RtcMessengerServer should check in ProcessData and ProcessMessage for isListening to simulate server behavior better.
Logged
Pages: [1] 2
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.03 seconds with 17 queries.