Advanced .NET Remoting:第 9 章

3.传递运行时信息

前面使用的接收器 ( Sink ) 是 IClientChannelSinks 与 IServerChannelSinks。这意味着它们工作在格式化器已经序列化 IMessage 对象 之后 。而对于 IMessageSink 来说,不同的是,可以直接工作在消息的内容被格式化 之前 。这意味着,你对 IMessage 内容的任何修改都将被序列化,进而反映到最终的流中。

注意:即使你试图在 IClienntChannelSink 中修改 IMessage 对象的内容,需要注意的是,这些修改 不会 被传播到服务器端,因为序列化的流已经通过 IMessage 对象生成了。

基于该区别,客户端 IMessageSink 可以用来从客户端将运行时信息传递到服务器端。在随后的示例中,我将向您展示如何将客户端的线程 ( thread ) 的当前优先级别 ( priority ) 传递到服务器端,以便远程方法可以运行在同样的优先级上。

为了从客户端向服务器端发送任意数据,你需要将它加入到 Message 对象的逻辑调用上下文 ( logical call context ) 中。通过这种方式,你可以传递可序列化对象 ( serializable ) 或者扩展了 MarshalByRefObject 的对象。例如,为了在对服务器上任何方法的调用中,传递客户端的线程的当前上下文,你可以实现如下的 SyncProcessMessage() 方法:

public IMessage SyncProcessMessage(IMessage msg)
{
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"]; lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.SyncProcessMessage(msg);
}
else
{
return _nextMsgSink.SyncProcessMessage(msg);
}
}

对于 AsyncProcessMessage() 方法也同样处理。

public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"]; lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
else
{
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
}

在服务器端,你也必须实现一个 IServerChannelSink 来从 IMessage 对象中提取调用上下文信息 ( call context ),然后设置到 Thread.CurrentThread.Priority 上来应用该值。

public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
LogicalCallContext lcc =
(LogicalCallContext) requestMsg.Properties["__CallContext"]; // storing the current priority
ThreadPriority oldprio = Thread.CurrentThread.Priority; // check if the logical call context contains "priority"
if (lcc != null && lcc.GetData("priority") != null)
{
// fetch the priority from the call context
ThreadPriority priority =
(ThreadPriority) lcc.GetData("priority"); Console.WriteLine(" -> Pre-execution priority change {0} to {1}",
oldprio.ToString(),priority.ToString()); // set the priority
Thread.CurrentThread.Priority = priority;
} // push on the stack and pass the call to the next sink
// the old priority will be used as "state" for the response
sinkStack.Push(this,oldprio);
ServerProcessing spres = _next.ProcessMessage (sinkStack,
requestMsg, requestHeaders, requestStream,
out responseMsg,out responseHeaders,out responseStream); // restore priority if call is not asynchronous
if (spres != ServerProcessing.Async)
{
if (lcc != null && lcc.GetData("priority") != null)
{
Console.WriteLine(" -> Post-execution change back to {0}",oldprio);
Thread.CurrentThread.Priority = oldprio;
}
}
return spres;
}

用于服务器端的接收器的接收器提供器非常简单。它看起来多少与前面的 IServerChannelSink 是相同的。

在客户端,这种方式有一点不太方便。请记住,你现在是在实现 IMessageSink 而不是 IClientChannelSing。如果寻找 IMessageSinkProvider 的话不会有 任何结果,所以,此时你还是不得不实现一个 IClientChannelSink - 即使实际上该连接器是 IMessageSink。当查看随后部分的 IClientChannelSinkProvider 接口的时候,就会发现这个问题。

IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData);

这个接口说明,在任何场景下,CreateSink() 都只会返回 IClientChannelSink,即使你的连接器只需要实现 IMessageSink。所以现在你不得不扩展你的 IMessageSink 同时去实现 IClientChannelSink。你还不得不使用条件判断,因为 IClientChannelSink 也定义来更多的方法需要实现。这些方法将在这个连接器作为通道连接器 ( channel sink ) 的时候而不是作为消息连接器 ( 也就是说,在格式化器之后 ) 被调用。 你可能不希望你的用户将该连接器放置在格式化器 之后 ( 它在这里并不有效,因为它修改了 IMessage 对象的内容 ),所以你会希望对于这些方法抛出异常。

完整的客户端 PriorityEmitterSink 代码,会在用于错误的顺序时抛出这些异常,如列表 13-12 所示:

using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Threading;
namespace PrioritySinks
{
public class PriorityEmitterSink : BaseChannelObjectWithProperties,
IClientChannelSink, IMessageSink
{
private IMessageSink _nextMsgSink;
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
// only for method calls
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
else
{
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
} public IMessage SyncProcessMessage(IMessage msg)
{
// only for method calls
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.SyncProcessMessage(msg);
}
else
{
return _nextMsgSink.SyncProcessMessage(msg);
}
} public PriorityEmitterSink (object next)
{
if (next as IMessageSink != null)
{
_nextMsgSink = (IMessageSink) next;
}
} public IMessageSink NextSink
{
get
{
return _nextMsgSink;
}
} public IClientChannelSink NextChannelSink
{
get
{
throw new RemotingException("Wrong sequence.");
} } public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
} public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
} public void AsyncProcessResponse(
IClientResponseChannelSinkStack sinkStack,
object state,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
} public System.IO.Stream GetRequestStream(IMessage msg,
ITransportHeaders headers)
{
throw new RemotingException("Wrong sequence.");
} public void ProcessMessage(IMessage msg,
ITransportHeaders requestHeaders,
Stream requestStream,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
throw new RemotingException("Wrong sequence.");
}
}
}

客户端的 PriorityEmitterSinkProvider 如列表 13-13 所示,实现很直接。只有方法 CreateSink() 比较值得关注。

列表 13-13 客户端的 PriorityEmitterSinkProvider

using System;
using System.Collections;
using System.Runtime.Remoting.Channels; namespace PrioritySinks
{
public class PriorityEmitterSinkProvider: IClientChannelSinkProvider
{
private IClientChannelSinkProvider next = null;
public PriorityEmitterSinkProvider(IDictionary properties,
ICollection providerData)
{
// not needed
} public IClientChannelSink CreateSink(IChannelSender channel,
string url, object remoteChannelData)
{
IClientChannelSink nextsink =
next.CreateSink(channel,url,remoteChannelData);
return new PriorityEmitterSink(nextsink);
} public IClientChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
}

列表 13-14 是服务器端的 IServerChannelSink 实现,与客户端不同,它不是 IMessageSink,所以该实现更为一致。你不需要在这里实现任何其它接口。

列表 13-14 服务器端的 PriorityChangerSink

using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging ;
using System.Runtime.Remoting.Channels;
using System.Threading;
namespace PrioritySinks
{
public class PriorityChangerSink : BaseChannelObjectWithProperties,
IServerChannelSink, IChannelSinkBase
{
private IServerChannelSink _next;
public PriorityChangerSink (IServerChannelSink next)
{
_next = next;
} public void AsyncProcessResponse (
IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
// restore the priority
ThreadPriority priority = (ThreadPriority) state;
Console.WriteLine(" -> Post-execution change back to {0}",priority);
Thread.CurrentThread.Priority = priority;
} public Stream GetResponseStream (IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage msg,
ITransportHeaders headers )
{
return null;
} public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
LogicalCallContext lcc =
(LogicalCallContext) requestMsg.Properties["__CallContext"];
// storing the current priority
ThreadPriority oldprio = Thread.CurrentThread.Priority;
// check if the logical call context contains "priority"
if (lcc != null && lcc.GetData("priority") != null)
{
// fetch the priority from the call context
ThreadPriority priority =
(ThreadPriority) lcc.GetData("priority");
Console.WriteLine("-> Pre-execution priority change {0} to {1}",
oldprio.ToString(),priority.ToString());
// set the priority
Thread.CurrentThread.Priority = priority;
} // push on the stack and pass the call to the next sink
// the old priority will be used as "state" for the response
sinkStack.Push(this,oldprio);
ServerProcessing spres = _next.ProcessMessage (sinkStack,
requestMsg, requestHeaders, requestStream,
out responseMsg,out responseHeaders,out responseStream); // restore priority if call is not asynchronous
if (spres != ServerProcessing.Async)
{
if (lcc != null && lcc.GetData("priority") != null)
{
Console.WriteLine("-> Post-execution change back to {0}",oldprio);
Thread.CurrentThread.Priority = oldprio;
}
}
return spres;
} public IServerChannelSink NextChannelSink
{
get {return _next;}
set {_next = value;}
}
}
}

列表 13-15 是相关的服务器端连接器提供器,它实现了接口 IServerChannelSinkProvider

列表 13-15 服务器端的 PriorityChangerSinkProvider

using System;
using System.Collections;
using System.Runtime.Remoting.Channels;
namespace PrioritySinks
{
public class PriorityChangerSinkProvider: IServerChannelSinkProvider
{
private IServerChannelSinkProvider next = null;
public PriorityChangerSinkProvider(IDictionary properties,
ICollection providerData)
{
// not needed
} public void GetChannelData (IChannelDataStore channelData)
{
// not needed
} public IServerChannelSink CreateSink (IChannelReceiver channel)
{
IServerChannelSink nextSink = next.CreateSink(channel);
return new PriorityChangerSink(nextSink);
} public IServerChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
}

为了测试该连接器的组合,使用如下的 SAO (服务器端激活对象) ,它会返回服务器端的当前线程的优先级:

public class TestSAO: MarshalByRefObject
{
public String getPriority()
{
return System.Threading.Thread.CurrentThread.Priority.ToString();
}
}

这个 SAO 将被客户端使用不同的线程优先级调用多次。服务器端使用的配置文件如下所示:

<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="5555">
<serverProviders>
<formatter ref="soap" />
<provider
type="PrioritySinks.PriorityChangerSinkProvider, PrioritySinks" />
</serverProviders>
</channel>
</channels>
<service>
<wellknown mode="Singleton"
type="Server.TestSAO, Server" objectUri="TestSAO.soap" />
</service>
</application>
</system.runtime.remoting>
</configuration>

客户端的配置文件如下所示:

<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<provider
type="PrioritySinks.PriorityEmitterSinkProvider, PrioritySinks" />
<formatter ref="soap" />
</clientProviders>
</channel>
</channels>
<client>
<wellknown type="Server.TestSAO, generated_meta"
url="http://localhost:5555/TestSAO.soap" />
</client>
</application>
</system.runtime.remoting>
</configuration>

在用于测试的客户端,你可以使用 SoapSuds 来抽取元数据。当你运行如列表 13-16 所示的应用程序的时候,将会看到如图 13-8 所示的输出。

using System;
using System.Runtime.Remoting;
using Server; // from generated_meta.dll
using System.Threading;
namespace Client
{
delegate String getPrioAsync();
class Client
{
static void Main(string[] args)
{
String filename = "client.exe.config";
RemotingConfiguration.Configure(filename);
TestSAO obj = new TestSAO();
test(obj); Thread.CurrentThread.Priority = ThreadPriority.Highest;
test(obj); Thread.CurrentThread.Priority = ThreadPriority.Lowest;
test(obj); Thread.CurrentThread.Priority = ThreadPriority.Normal;
test(obj); Console.ReadLine();
} static void test(TestSAO obj)
{
Console.WriteLine("----------------- START TEST CASE ---------------");
Console.WriteLine(" Local Priority: {0}",
Thread.CurrentThread.Priority.ToString());
String priority1 = obj.getPriority();
Console.WriteLine(" Remote priority: {0}",priority1.ToString());
Console.WriteLine("----------------- END TEST CASE ---------------");
}
}
}

图 13-8 通过客户端测试的输出,展示了连接器如愿工作

参考资料

Advanced .NET Remoting: 第 9 章 3.在 Remoting 中传递额外的运行时信息的更多相关文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  2. 第十一章:WEB浏览器中的javascript

    客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...

  3. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  4. NodeJs>------->>第二章:Node.js中交互式运行环境--------REL

    第二章:Node.js中交互式运行环境--------REL 一:REPL运行环境概述 C:\Users\junliu>node > foo = 'bar' ; 'bar' > 二: ...

  5. ANTLR4权威指南 - 第6章 尝试一些实际中的语法

    第6章 尝试一些实际中的语法 在前一章,我们学习了通用词法结构和语法结构,并学习了如何用ANTLR的语法来表述这些结构.现在,是时候把我们学到的这些用来构建一些现实世界中的语法了.我们的主要目标是,怎 ...

  6. “全栈2019”Java第九十七章:在方法中访问局部内部类成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. “全栈2019”Java第八十五章:实现接口中的嵌套接口

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. “全栈2019”Java第二十七章:流程控制语句中循环语句for

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. “全栈2019”Java第二十六章:流程控制语句中循环语句do-while

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. “全栈2019”Java第二十五章:流程控制语句中循环语句while

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. LeetCode 1000. Minimum Cost to Merge Stones (区间 DP)

    根据寒神题解 https://leetcode.com/problems/minimum-cost-to-merge-stones/discuss/247567/JavaC%2B%2BPython-D ...

  2. foobar2000 v2.1.2 汉化版(更新日期: 2024.02.27)

    新春佳节,送上一份新年礼物,祝您在新的一年里,万事如意,心想事成,身体健康,事业有成,财源广进,家庭和睦,笑容常开,好运连连.     foobar2000 v2.1.2 汉化版 ---------- ...

  3. 冒泡排序和一点优化(php)

    function maopao($arr){ $len = count($arr);//获取数组的长度 //有多少个数组元素就最多就要排n-1次 for ($j=0;$j<$len-1;$j++ ...

  4. Android 常用的性能分析工具详解:GPU呈现模式

    此篇将重点介绍几种常用的Android性能分析工具: 一.Logcat 日志 选取Tag=ActivityManager,可以粗略地知道界面Displaying的时间消耗.当我们打开一个Activit ...

  5. 2024年5月中国数据库排行榜:OP持续领跑,GoldenDB稳步上升进前八

    入夏时节,2024年5月的中国流行度排行榜排行榜如期发布.在这个骄阳似火的季节,各大数据库产品之间的竞争愈发激烈,名次间的细微变动展示了市场的动态和活力.各家厂商不断创新,通过技术升级和性能优化,力求 ...

  6. kotlin更多语言结构——>选择加入要求

    1.3.70 中引入了 @RequireOptIn 与 @OptIn 注解以取代先前使用的 @Experimental 与 @UseExperimental;同时 -Xopt-in 编译器选项也取代了 ...

  7. ASP.NET实现网站发布及跨域访问

    1.软件下载及安装 visual studio 2012 or 2013 启用电脑IIS配置 2.网页编写及排版 在visual studio中创建web项目添加aspx页面(个人网页:和html差不 ...

  8. 还在使用昂贵的虚拟机?来试试 Devbox,便宜 6 倍!

    这篇小短文来介绍一下用虚拟机的场景是怎么被 Devbox 全方位碾压的. Devbox 唯一弱点是公网出口的地方不分配独立的 IP 地址,但是这对我们绝大多数场景是没有影响的,通过域名和端口访问我们的 ...

  9. 云原生周刊:12 个容易忽略的 Kubernetes 安装错误

    文章推荐 12 个容易忽略的 Kubernetes 安装错误 这篇文章总结了 12 个在 Kubernetes 安装过程中容易忽略但却必须注意的错误.这些错误包括: 没有正确安装 kubectl. 没 ...

  10. 【2024】所有人都能看懂的 Win 11 安装/重装教程,跟着我一遍包成功

    无论你因为系统坏掉想重装一下 Windows,或者只是想升级一下 Windows 都可以.虽然标题写的是 Win 11,不过实际上对于任何 Windows 系统都适用,不过现在 Win 11 已经相当 ...