• Advanced .NET Remoting: 第 9 章 4.改变编程模型


    Advanced .NET Remoting: 第 9 章

    4.改变编程模型

    前面的所有连接器在 .NET Remoting 应用程序的服务器端和客户端两方面增强功能。可插拔的连接器架构不仅支持创建连接器,它还改变了编程模型的多个方面。例如,在第 5 章,你已经见到了为了传递像用户名和口令这样的认证凭据,导致手工修改每个对象的通道连接器的属性。

    CustomerManager mgr = new CustomerManager();
    IDictionary props = ChannelServices.GetChannelSinkProperties(mgr);
    props["username"] = "dummyremotinguser";
    props["password"] = "12345";
    

    不过,在大多数真实世界的应用中,最好基于每个主机设置这些属性,或根据目标对象的基础 URL 设置它们。在理想情况下,使用配置文件或代码可以做到这一点,如以下示例所示:

    <configuration>
        <system.runtime.remoting>
            <application>
                <channels>
                    <channel ref="http">
                        <clientProviders>
                            <formatter ref="soap" />
                            <provider type="UrlAuthenticationSink.UrlAuthenticationSinkProvider, UrlAuthenticationSink">
                                <url
                                    base="http://localhost"
                                    username="DummyRemotingUser"
                                    password="12345"
                                />
                                <url
                                    base="http://www.somewhere.org"
                                    username="MyUser"
                                    password="12345"
                                />
                            </provider>
                        </clientProviders>
                    </channel>
                </channels>
            </application>
        </system.runtime.remoting>
    </configuration>
    

    当使用代码进行设置的时候,你可以简化配置文件中使用的 <url> 条目,使用下面的代码来达到相同的目的。

    UrlAuthenticator.AddAuthenticationEntry(
        "http://localhost",
        "dummyremotinguser",
        "12345");
    
    UrlAuthenticator.AddAuthenticationEntry(
        "http://www.somewhere.org",
        "MyUser",
        "12345");
    

    不过,默认并不支持这种方式。可以通过使用自己定制实现的 IClientChannelSink 来轻松实现。

    在真正处理连接器之前,你必须编写一个助理类,它提供静态方法用来存储和提取针对基础 URL 的验证条目。所有的条目以 ArrayList 的形式存储,可以通过提供给方法 GetAuthenticationEntry() 方法的 URL 参数来提取。另外,如果对于特定的基础 URL 来说,没有匹配的话,将返回默认的认证信息。助理类如列表 13-17 所示。

    列表 13-17 存储用户名和口令的 UrlAuthenticator 助理类

    using System;
    using System.Collections;
    namespace UrlAuthenticationSink
    {
        internal class UrlAuthenticationEntry
        {
            internal String Username;
            internal String Password;
            internal String UrlBase;
            internal UrlAuthenticationEntry (String urlbase,
                String user,
                String password)
            {
                this.Username = user;
                this.Password = password;
                this.UrlBase = urlbase.ToUpper();
            }
        }
    
        public class UrlAuthenticator
        {
            private static ArrayList _entries = new ArrayList();
            private static UrlAuthenticationEntry _defaultAuthenticationEntry;
    
            public static void AddAuthenticationEntry(String urlBase,
                String userName,
                String password)
            {
                _entries.Add(new UrlAuthenticationEntry(
                    urlBase,userName,password));
            }
    
            public static void SetDefaultAuthenticationEntry(String userName,
                String password)
            {
                _defaultAuthenticationEntry = new UrlAuthenticationEntry(
                    null,userName,password);
            }
    
            internal static UrlAuthenticationEntry GetAuthenticationEntry(String url)
            {
                foreach (UrlAuthenticationEntry entr in _entries)
                {
                    // check if a registered entry matches the url-parameter
                    if (url.ToUpper().StartsWith(entr.UrlBase))
                    {
                        return entr;
                    }
                }
    
                // if none matched, return the default entry (which can be null as well)
                return _defaultAuthenticationEntry;
            }
        }
    }
    

    连接器本身调用其中的方法来检查对于当前消息的 URL 来说,是否存在一个认证条目。然后,它遍历连接器调用链,直到最后一个传输通道连接器,在这里设置包含正确用户名和口令的属性。最终,为该对象的连接器设置一个标志,以便该逻辑对于一个连接器链条来说只应用一次。该连接器的完整代码如下列表 13-18 所示。

    列表 13-18 UrlAuthenticationSink

    using System;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Messaging;
    using System.IO;
    namespace UrlAuthenticationSink
    {
        public class UrlAuthenticationSink: BaseChannelSinkWithProperties,
            IClientChannelSink
        {
            private IClientChannelSink _nextSink;
            private bool _authenticationParamsSet;
    
            public UrlAuthenticationSink(IClientChannelSink next)
            {
                _nextSink = next;
            }
    
            public IClientChannelSink NextChannelSink
            {
                get {
                    return _nextSink;
                }
            }
    
            public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
                IMessage msg,
                ITransportHeaders headers,
                Stream stream)
            {
                SetSinkProperties(msg);
                
                // don't push on the sinkstack because this sink doesn't need
                // to handle any replies!
                _nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
            }
    
            public void AsyncProcessResponse(
                IClientResponseChannelSinkStack sinkStack,
                object state,
                ITransportHeaders headers,
                Stream stream)
            {
                // not needed
            }
    
            public Stream GetRequestStream(IMessage msg,
                ITransportHeaders headers)
            {
                return _nextSink.GetRequestStream(msg, headers);
            }
    
            public void ProcessMessage(IMessage msg,
                ITransportHeaders requestHeaders,
                Stream requestStream,
                out ITransportHeaders responseHeaders,
                out Stream responseStream)
            {
                SetSinkProperties(msg);
                _nextSink.ProcessMessage(msg,requestHeaders,requestStream,
                    out responseHeaders,out responseStream);
            }
    
            private void SetSinkProperties(IMessage msg)
            {
                if (! _authenticationParamsSet)
                {
                    String url = (String) msg.Properties["__Uri"];
                    UrlAuthenticationEntry entr =
                        UrlAuthenticator.GetAuthorizationEntry(url);
                    if (entr != null)
                    {
                        IClientChannelSink last = this;
                        while (last.NextChannelSink != null)
                        {
                            last = last.NextChannelSink;
                        }
    
                        // last now contains the transport channel sink
                        last.Properties["username"] = entr.Username;
                        last.Properties["password"] = entr.Password;
                    }
                    _authenticationParamsSet = true;
                }
            }
        }
    }
    

    相关联的连接器提供器提供 <url> 条目,它们可以在下面的连接器提供器中,通过配置文件指定。

    <provider type="UrlAuthenticationSink.UrlAuthenticationSinkProvider,
        UrlAuthenticationSink">
        <url
            base="http://localhost"
            username="DummyRemotingUser"
            password="12345"
        />
    </provider>
    

    连接器提供器将通过 providerData 集合接收到这些条目。集合中包括 SinkProviderData 类型的对象实例。每个 SinkProdiverData 对象包含一个对 properties 的字典,它支持访问这些每个条目中的属性 ( 基础 URL、用户名、口令 )。

    当配置文件中的条件设置了基础 URL 的时候,它简单地调用 UrlAuthenticator.AddAuthenticationEntry() 方法。如果没有指定基础 URL,就将这里的用户名和口令设置为默认认证值。该提供器的完整代码如列表 13-19 所示。

    列表 13-19 UrlAuthenticationSinkProvider

    using System;
    using System.Runtime.Remoting.Channels;
    using System.Collections;
    
    namespace UrlAuthenticationSink
    {
        public class UrlAuthenticationSinkProvider: IClientChannelSinkProvider
        {
            private IClientChannelSinkProvider _nextProvider;
            public UrlAuthenticationSinkProvider(IDictionary properties,
                ICollection providerData)
            {
                foreach (SinkProviderData obj in providerData)
                {
                    if (obj.Name == "url")
                    {
                        if (obj.Properties["base"] != null)
                        {
                            UrlAuthenticator.AddAuthenticationEntry(
                                (String) obj.Properties["base"],
                                (String) obj.Properties["username"],
                                (String) obj.Properties["password"]);
                        }
                        else
                        {
                            UrlAuthenticator.SetDefaultAuthenticationEntry(
                                (String) obj.Properties["username"],
                                (String) obj.Properties["password"]);
                        }
                    }
                }
            }
    
            public IClientChannelSinkProvider Next
            {
                get {return _nextProvider; }
                set {_nextProvider = value;}
            }
    
            public IClientChannelSink CreateSink(IChannelSender channel,
                string url,
                object remoteChannelData)
            {
                // create other sinks in the chain
                IClientChannelSink next = _nextProvider.CreateSink(channel,
                    url,
                    remoteChannelData);
    
                // put our sink on top of the chain and return it
                return new UrlAuthenticationSink(next);
            }
        }
    }
    

    使用该连接器

    在需要使用这个连接器的时候,你可以通过在配置文件中简单地将它添加到客户端的连接器链中,如下所示:

    <configuration>
        <system.runtime.remoting>
            <application>
                <channels>
                    <channel ref="http">
                        <clientProviders>
                            <formatter ref="soap" />
                            <provider type="UrlAuthenticationSink.UrlAuthenticationSinkProvider,
                                UrlAuthenticationSink" />
                        </clientProviders>
                    </channel>
                </channels>
            </application>
        </system.runtime.remoting>
    </configuration>
    

    注意:该连接器是 IClientChannelSink,所以必须放在格式化器 之后

    为了对特定的基础 URL 指定一组用户名和密码,现在可以通过将认证信息添加到配置文件中来完成,将一个或者多个 <url> 条件添加到 <provider> 配置节来。

    <clientProviders>
        <formatter ref="soap" />
        <provider type="UrlAuthenticationSink.UrlAuthenticationSinkProvider,
            UrlAuthenticationSink">
            <url
                base="http://localhost"
                username="DummyRemotingUser"
                password="12345"
            />
        </provider>
    </clientProviders>
    

    如果你不希望硬编码这些信息,你可以让客户端应用程序的用户提供用户名和密码,然后使用下面的代码来为连接器注册它:

    UrlAuthenticator.AddAuthenticationEntry(<url>, <username>, <password>);
    

    为了达到与前面使用配置文件片断中的 <url> 所示的相同效果,你可以如下使用:

    UrlAuthenticator.AddAuthenticationEntry(
        "http://localhost",
        "dummyremotinguser",
        "12345");
    

    参考资料

  • 相关阅读:
    Java字符串操作
    easyui Combotree根据用户输入显示对应的tree值
    maven
    引用 js表单验证大全 以后方便查看用
    对象内存模型
    高级性能服务器编程模型【IOCP完成端口】开发实现【一】
    高级性能服务器编程模型【IOCP完成端口】开发实现【二】
    高级性能服务器编程模型【IOCP完成端口】开发实现【三】
    探讨【IGE】的源代码【六】,承接【五】,内存池管理。
    hive beeline详解
  • 原文地址:https://www.cnblogs.com/haogj/p/16333497.html
Copyright © 2020-2023  润新知