2014年5月13日星期二

在任意地方,得到socket的信息

用java写servlet时,随时我们都可以得到HttpServletRequest,HttpServletResponse以及session,那么在datasnap里面呢,比如我在服务里面知道当前是哪个ip在call我的服务
跟踪代码,发现session是可以得到,但是比如我打算枚举session的内容,缺没提供方法了。于是,我使用RTTI的盗窃法子,读取了私有变量
procedure RttiGetPrivateValue(AClass: TClass; AInstance: TObject; FieldName: string; var Value: TObject);
var
  ref:  TRttiContext;
  typ: TRttiType;
  mthd: TRttiMethod;
  fld: TRttiField;
begin
  typ := ref.GetType(AClass);
  fld := typ.GetField(FieldName); //can get private value
  Value := fld.GetValue(AInstance).AsObject;
end;

procedure EnumSessionData(const Session: TDSSession);
var
  SessionData: TDSSessionDictionaryData;
  MetaData: TDictionary;
  MetaObjects: TDictionary;
  Key: string;
begin
  RttiGetPrivateValue(TDSSession, Session, 'FSessionData', TObject(SessionData));
  RttiGetPrivateValue(TDSSessionDictionaryData, SessionData, 'FMetaData', TObject(MetaData));
  RttiGetPrivateValue(TDSSessionDictionaryData, SessionData, 'FMetaObjects', TObject(MetaObjects));
  for Key in MetaData.Keys do
    log.Debug('Sesion[%s] = %s', [Key, MetaData[Key]]);
  for Key in MetaObjects.Keys do
    log.Debug('SesionObjects[%s] = %s', [Key, MetaObjects[Key].ClassName]);
end;

function TdmServer.DSServerTrace(TraceInfo: TDBXTraceInfo): CBRType;
var
  Session: TDSSession;
begin
  log.TrackMethod('TdmServer.DSServerTrace');
  Session := TDSSessionManager.GetThreadSession;
  if Session.ObjectCreator <> nil then
    log.Debug(Session.ObjectCreator.ClassName);
  log.Debug(TraceInfo.Message);
  EnumSessionData(Session); //在这里试着枚举读取一下
  Result := cbrUSEDEF;
end;

可以看到,Session里面对于Tcp,默认的只有两个变量,
Sesion[remoteip] = 192.168.101.11
Sesion[communicationprotocol] = tcp/ip
这里的remoteip就是调用者ip了,但是如果想到得到sessioin对应的tunnel的具体socket信息,缺是不能了。 这两个值是在 TDSTCPServerTransport.DoOnConnect里放入的,代码如下
procedure TDSTCPServerTransport.DoOnConnect(AContext: IIPContext);
var
  IndyChannel: TDBXChannel;
  FilterChannel: TDBXFilterSocketChannel;
  Event: TDSTCPConnectEventObject;
begin
  FilterChannel := TDBXFilterSocketChannel.Create(Filters);

  IndyChannel := CreateTcpChannel(AContext);

{$IFNDEF POSIX}
  if CoInitFlags = -1 then
    CoInitializeEx(nil, COINIT_MULTITHREADED)
  else
    CoInitializeEx(nil, CoInitFlags);
{$ENDIF}
  IndyChannel.Open;

  // set the delegate
  FilterChannel.Channel := IndyChannel;

  AContext.Data := FProtocolHandlerFactory.CreateProtocolHandler(FilterChannel);
  if AContext.Data is TDBXJSonServerProtocolHandler then
  begin
    if TDBXJSonServerProtocolHandler(AContext.Data).DSSession = nil then
    begin
      TDBXJSonServerProtocolHandler(AContext.Data).DSSession :=
        TDSSessionManager.Instance.CreateSession(
          function: TDSSession
          begin
            Result := TDSTCPSession.Create(AuthenticationManager);
            Result.PutData('CommunicationProtocol', 'tcp/ip'); //这里放了进去
            Result.PutData('RemoteIP', AContext.Connection.Socket.Binding.PeerIP); //这里放了进去信息
          end,
          ''
        );
    end;
  end;

  if Assigned(FTDSTCPConnectEvent) and (AContext <> nil) and (AContext.Connection <> nil) and
    (FTcpServer <> nil) and FTcpServer.Active then
  begin
    if IndyChannel Is TDSTCPChannel then
    begin
      //enable keep alive, disable it, or leave the OS default setting as it is
      if FKeepAliveEnablement = kaEnabled then
        TDSTCPChannel(IndyChannel).EnableKeepAlive(FKeepAliveTime, FKeepAliveInterval)
      else if FKeepAliveEnablement = kaDisabled then
        TDSTCPChannel(IndyChannel).DisableKeepAlive;

      Event := TDSTCPConnectEventObject.Create(AContext.Connection.GetObject, TDSTCPChannel(IndyChannel));
    end
    else
      Event := TDSTCPConnectEventObject.Create(AContext.Connection.GetObject, nil);

    try
      OnConnect(Event);
    except
    end;
  end;
end;

留意一下key的大小写进去的是RmoteIp,出来却变成了remoteip了,所以默认的Session是不关系大小写的,这是因为TDictionary创建是并没指定TStringComparer.Ordinal。目前DataSnap还不支持自定义TDSSessionData的派生,因为TDSSession.CreateSessionData不是虚的。

没有评论:

发表评论