RTC Forums
April 25, 2024, 09:06:02 AM *
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 19469 times)
Kas
RTC Expired
*
Posts: 17


« Reply #15 on: July 25, 2018, 04:23:22 PM »

This will work too if i kept a list GetConnection and called them with :

Code:
procedure WakeUpByConnection(conn:TRtcConnection);
  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
            if  TRtcDelayedCall(ob).Conn=conn then
              TRtcDelayedCall(ob).WakeUp;
            i:=List.search_g(i, ob);
          end;
      end;
  finally
    CS.Release;
    end;
end;
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #16 on: July 25, 2018, 04:36:10 PM »

Since (A) all delayed call objects are kept in a single global list and (B) the only reason to force a wake-up call on all delayed call objects would be to make sure that blocked threads waiting for a wake-up call get "unblocked" before the application terminates, I think that adding a global "WakeUpAllDelayedCalls" procedure is the simplest solution to this problem, so ... I'm going to add it to the "rtcSrvModule.pas" unit in the next RTC SDK update.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #17 on: July 25, 2018, 04:56:15 PM »

Thank you Danijel, that is great.

But may adding (B) will be helpful too !

if TRtcMessengerServer kept a list of only delayed connections and be used like this


Code:
 
procedure TRtcMessageServer.ProcessData(aRequest, aReply: TStream);
  begin
  EnterEvent;
  try
    FConList.Add(Con);
    TMyProvider(Con).ExecuteRequest(aRequest, aReply);
    FConList.Remove(Con);
  finally
    LeaveEvent;
    end;
  end;

Then TRtcMessageServer can wake only the blocked calls, calling (B)  from TRtcMessengerServer Disconnect and Destroy will resolve all the specific problem that comes with TRtcMessengerServer simulations.
and (A) is helpful in all.

Thank you again ,i see you already updated the version, while i still writing a post !, and that is awesome.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #18 on: July 25, 2018, 05:02:00 PM »

Even though delayed call objects need a reference to the Connection object from which they were initiated, Connection components (like TRtcMessageServer) do NOT have any knowledge about Delayed Calls. Delayed Call objects are implemented at a higher level and used ONLY by the "TRtcServerModule" component.

Anyway ... I've released the update with the "WakeUpAllDelayedCalls" procedure in the "rtcSrvModule.pas" unit. Since this is also the unit where the "TRtcServerModule" component is implemented, it will be in the "uses" clause of any unit implementing server-side remote functions, so calling "WakeUpAllDelayedCalls" from the "finalize" section of a unit where delayed calls are used would probably be the "cleanest" solution.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #19 on: July 25, 2018, 05:11:30 PM »

Thank you very much, that is enough.
I will prepare an example ( simple and easy ) and post it here.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #20 on: July 25, 2018, 06:21:20 PM »

Code:
unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcConn, rtcSrvModule,
  rtcPortalGate, rtcSystem, rtcInfo, rtcMsgSrv, rtcTcpSrv, Vcl.StdCtrls,
  rtcDataSrv;

type
  TForm6 = class(TForm)
    RtcMessageServer1: TRtcMessageServer;
    RtcTcpServer1: TRtcTcpServer;
    Gateway: TRtcPortalGateway;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure RtcMessageServer1Disconnect(Sender: TRtcConnection);
    procedure RtcTcpServer1Connect(Sender: TRtcConnection);
    procedure RtcTcpServer1DataReceived(Sender: TRtcConnection);
    procedure RtcTcpServer1Disconnect(Sender: TRtcConnection);
  private
  public
  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

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

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

procedure TForm6.RtcMessageServer1Disconnect(Sender: TRtcConnection);
begin
  TRtcConnection(Sender.Info.asObj['parCon']).Disconnect;
  Sender.Info.asObj['parCon'] := nil;
end;

procedure TForm6.RtcTcpServer1Connect(Sender: TRtcConnection);
var
  Conn: TRtcMessageServer;
begin
  Conn := TRtcMessageServer(RtcMessageServer1.GetConnection);
  Conn.Info.asObj['parCon'] := Sender;
  Sender.Info.asObj['con'] := Conn;
  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(CRLF, 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;
  conn: TRtcMessageServer;
begin
  if not RtcMessageServer1.isListening then
  begin
    Sender.Disconnect;
    Exit;
  end;

  aReq := TMemoryStream(Sender.Info.asObj['req']);
  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;
  aAns := TMemoryStream(Sender.Info.asObj['res']);
  conn := TRtcMessageServer(Sender.Info.asObj['con']);
  conn.ProcessData(aReq, aAns);
  if aAns.Size > 0 then
  begin
    aAns.Position := 0;
    SetLength(buf, aAns.Size);
    aAns.ReadBuffer(buf[0], aAns.Size);
    if not Application.Terminated then
      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.

Things to know about this example:
RTC ProcessData does support partial request , but i kept the code above as an example.
It assume the HTTP header (only the header not the whole request ) is not fragmented or will disconnect , this should not be a problem as fragmentation is very unlikely for the first MTU size (around 1500 bytes) when sending, so while the header size is less than MTU we are safe .
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #21 on: July 25, 2018, 07:06:12 PM »

Ignore my question about your example code (question deleted). I've just realized that you are calculating the size of the complete request stream (including request headers) in the "rs" variable and not just the request content body.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #22 on: July 25, 2018, 07:11:03 PM »

rs is the request size in full ( header and content length) ,if received length is more than one request then this is not standard HTTP protocol ( 1.0 and 1.1 ) and should disconnect , right ?
Because ProcessData is simple procedure and doesn't return if the connection should be dropped or how seek the request stream to the full request been received currently ProcessData will eat the extra data and do nothing.

Try this
  aReq.WriteBuffer(buf[0], 1);
  aReq.Position := 0;
  aAns := TMemoryStream(Sender.Info.asObj['res']);
  conn := TRtcMessageServer(Sender.Info.asObj['con']);
  conn.ProcessData(aReq, aAns);

it will work like nothing happen.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #23 on: July 25, 2018, 07:17:32 PM »

OK, so it's a quick "request format" check.
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #24 on: July 25, 2018, 07:24:44 PM »

Yes exactly.
This will do the job, while waiting for your RTC SDK to support HTTP2 Wink with full (http pipelining)
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #25 on: July 25, 2018, 07:49:26 PM »

I'm not sure I follow. What do you mean by "waiting for your RTC SDK to support HTTP2"? You know that HTTP/2 is only going to be implemented on HTTP connection components (TRtcHttpServer and TRtcHttpClient). Right?
Logged
Kas
RTC Expired
*
Posts: 17


« Reply #26 on: July 25, 2018, 08:21:11 PM »

I imagined the feeding mechanism for requests as stream and the way delayed call internally will be huge change, and this will affect all components including RtcMessageServer, i imagined you will centralize the request handling code ( HTTP1 and HTTP2 ) and use it cross components components, i thought you will let the TRtcRequest build itself from the data received or a special builder class to help with HTTP2 pipelining, but it seems you will build it only for HTTP server and client.
In all cases i wish you good luck.
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.026 seconds with 16 queries.