.net remoting和wcf自托管——一个bug引发的警示
一、解决问题,需要深入,并从细节入手,多从代码找原因,不能认为代码是死的,不会出错:
之前代码都运行良好,突然某一天,在我电脑上出问题了。出了问题,那就应该找出原因。其实这个问题,本身并不难,好歹给你报出了个错:
获取Word远程代理服务失败:无法加载类型“clr:NoteFirst.KMS.Clients.RomoteInterface.IOfficeService, NoteFirst.KMS.Clients.RomoteInterface”。,
Server stack trace:
在 System.Runtime.Remoting.Messaging.MethodCall.ResolveMethod(Boolean bThrowIfNotResolved)
在 System.Runtime.Remoting.Messaging.MethodCall.HeaderHandler(Header[] h)
在 System.Runtime.Serialization.Formatters.Soap.ObjectReader.ParseObject(ParseRecord pr)
在 System.Runtime.Serialization.Formatters.Soap.SoapHandler.StartChildren()
在 System.Runtime.Serialization.Formatters.Soap.SoapParser.ParseXml()
在 System.Runtime.Serialization.Formatters.Soap.SoapParser.Run()
在 System.Runtime.Serialization.Formatters.Soap.ObjectReader.Deserialize(HeaderHandler handler, ISerParser serParser)
在 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Deserialize(Stream serializationStream, HeaderHandler handler)
在 System.Runtime.Remoting.Channels.CoreChannel.DeserializeSoapRequestMessage(Stream inputStream, Header[] h, Boolean bStrictBinding, TypeFilterLevel securityLevel)
在 System.Runtime.Remoting.Channels.SoapServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg,
ITransportHeaders& responseHeaders, Stream& responseStream)
net remoting在调用定义的接口时报错,无法加载类型,这错误是个什么样的错误,怎么就不能加载了,之前都好好的。为了解决这个问题,我花了一天多的时间。从系统运行环境,到office重新安装,折腾了个遍,就差装系统了。都说出了问题,从内部找原因,可是同事机器上的代码运行良好,我们的代码绝对一致。于是,我把目光就聚焦到外部环境上了。不过话说回来,外部环境也是有点问题的,比如安装了多个版本的office。在安装和卸载的频繁操作之下,很难知道注册表会不会出问题。
到了第二天,我就去改改代码,试着用另外一种方法解决问题。结果改着改着,就发现了代码原来是有bug的。前辈的代码,看似高深,调用了c++的很多方法。
TcpChannel tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(OfficeServiceImplement), CHANNEL_NAME, WellKnownObjectMode.SingleCall); EventLog.WriteEntry("NoteFirst", "注册tcp remote服务成功");
之前remoting采用的是http通道,我给改成tcp通道,结果问题就解决了。我就想,仅仅是通道不同,就会解决问题吗,所以想着http通道肯定是可以的。
channel = new HttpServerChannel(CHANNEL_NAME, GetEnablePort(), Provider);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(OfficeServiceImplement), OBJECT_URI, WellKnownObjectMode.Singleton);
看下GetEnablePort的定义:
private static int GetEnablePort()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int result = ;
while (true)
{
try
{
socket.Bind(new IPEndPoint(IPAddress.Any, result)); socket.Listen(); socket.Close(); ShareDataRW.OfficeAddinServicesPort = result; break;
}
catch
{
++result;
}
} return result;
}
动态获取了端口,并有赋值操作:ShareDataRW.OfficeAddinServicesPort = result;
service = Activator.GetObject(typeof(IOfficeService), string.Format(OfficeService.ServiceUrl, ShareDataRW.OfficeAddinServicesPort)) as IOfficeService;
这个是客户端调用remoting的代码,看看 ShareDataRW.OfficeAddinServicesPort 端口是怎么获取的:
public static int OfficeAddinServicesPort
{
get
{
return ReadShareDataStruct().OfficeAddinServicesPort;
}
set
{
ShareData sd = ReadShareDataStruct();
sd.OfficeAddinServicesPort = value;
WriteReadShareDataStruct(sd);
}
}
这里又引入了几个方法:
//将数据从非托管内存块封送到新分配的指定类型的托管对象
private static ShareData ReadShareDataStruct()
{
return (ShareData)Marshal.PtrToStructure(ShareDataMemoryPoint, ShareDataType);
}
//将数据从托管对象封送到非托管内存块中
private static void WriteReadShareDataStruct(ShareData data)
{
Marshal.StructureToPtr(data, ShareDataMemoryPoint, false);
}
ShareData是个结构体:
[StructLayout(LayoutKind.Sequential)]
private struct ShareData
{
public int ClientServicesPort;
public int OfficeAddinServicesPort;
public int WpsAddinServicesPort;
public int MainWindowsHandle;
}
Type ShareDataType = typeof(ShareData);
ShareDataMemoryPoint因为牵扯到c++里面的东西,不过从字面上看,共享内存地址,我猜的。看了这么多代码,我们大致理解,它是通过共享内存实现的端口存放,那为什么服务器端存进去的端口和客户端取出来的端口就不一样呢?这是我的疑惑点。为什么之前的代码就没有发生过这样的事情,请不要
老提过去好不好,代码是动态运行的,内存当中的活动也是动态的。有一种可能性,就是发布服务的端口在代码执行到那句的时候已经定好了,并把它写到内存中了。等客户端再去拿的时候,在这之前值被动了手脚。至于谁修改了它,什么时候修改的,这将是一个秘密,等待探寻。 二、WCF实现: 在这漫长的解决问题当中,我无意间看到微软的建议:把.net remoting迁移到wcf中。微软给出了具体的迁移步骤,特别详细,于是我就改写了代码,用wcf去实现: 定义协议
[ServiceContract]
public interface IOfficeService
{
[OperationContract]
void InsertTo(Bibliography[] bibliographies); [OperationContract]
IntPtr GetActiveDocumentWindowHandle(); [OperationContract] void Insert(string stream); /// <summary>
/// 获取文档的初始化时间
/// </summary>
/// <returns></returns>
[OperationContract]
DateTime GetDateTimeOfActivedDocument();
}
注意:方法不能同名
怎么实现并不重要,想怎么实现就怎么实现,我只管定义接口,这是发布服务,自托管服务:
NetTcpBinding binding = new NetTcpBinding();
Uri baseAddress = new Uri("net.tcp://localhost:8099/wcfserver"); ServiceHost serviceHost = new ServiceHost(typeof(OfficeServiceImplement), baseAddress);
serviceHost.AddServiceEndpoint(typeof(IOfficeService), binding, baseAddress);
serviceHost.Open(); EventLog.WriteEntry("NoteFirst", string.Format("The WCF server is ready at {0}", baseAddress));
再来看看客户端的调用:
NetTcpBinding binding = new NetTcpBinding();
String url = "net.tcp://localhost:8099/wcfserver";
EndpointAddress address = new EndpointAddress(url);
ChannelFactory<IOfficeService> channelFactory = new ChannelFactory<IOfficeService>(binding, address);
service = channelFactory.CreateChannel();
拿到service,即远程对象的代理,我们就可以调用接口中的方法了。
注意:实际代码中,需要考虑通道的释放等问题。
.net remoting和wcf自托管——一个bug引发的警示的更多相关文章
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- z-index失效原因分析——由一个bug引发的对层叠上下文和z-index属性的深度思考
新年刚开工就被一个bug虐得整个人都不好了,特地记录下. (一)bug描述 在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段提示的特效,所以做了一个提示层,但 ...
- MyBatis 学习记录7 一个Bug引发的思考
主题 这次学习MyBatis的主题我想记录一个使用起来可能会遇到,但是没有经验的话很不好解决的BUG,在特定情况下很容易发生. 异常 java.lang.IllegalArgumentExceptio ...
- MySQL 5.6的一个bug引发的故障
突然收到告警,提示mysql宕机了,该服务器是从库.于是尝试登录服务器看看能否登录,发现可以登录,查看mysql进程也存在,尝试登录提示 ERROR (HY000): Too many connect ...
- Hexo next博客的pjax一个Bug引发的关于pjax用法的小技巧-----pjax后图片点击放大的js失效
文章目录 广告: 背景 发现 解决 get技能 广告: 本人博客地址:https://mmmmmm.me 源码:https://github.com/dataiyangu/dataiyangu.git ...
- .Net remoting, Webservice,WCF,Socket区别
传统上,我们把计算机后台程序(Daemon)提供的功能,称为"服务"(service).比如,让一个杀毒软件在后台运行,它会自动监控系统,那么这种自动监控就是一个"服务& ...
- 大比速:remoting、WCF(http)、WCF(tcp)、WCF(RESTful)、asp.net core(RESTful)
近来在考虑一个服务选型,dotnet提供了众多的远程服务形式.在只考虑dotnet到dotnet的情形下,我们可以选择remoting.WCF(http).WCF(tcp).WCF(RESTful). ...
- 关于通信的关键词UDP/(TCP/IP)/IPC/RPC/.NET Remoting/WebService/WCF/Http 系列
OSI七层和TCP/IP四层的关系 1.1 OSI引入了服务.接口.协议.分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型. 1.2 OSI先有模型,后有协议,先有标准,后进行实践: ...
- 大比速:remoting、WCF(http)、WCF(tcp)、WCF(RESTful)、asp.net core(RESTful) .net core 控制台程序使用依赖注入(Autofac)
大比速:remoting.WCF(http).WCF(tcp).WCF(RESTful).asp.net core(RESTful) 近来在考虑一个服务选型,dotnet提供了众多的远程服务形式.在只 ...
随机推荐
- Linux基本命令 帮助命令
命令名称:man 英文原意:manual 命令所在路径:/usr/bin/man 执行权限:所有用户 语法:man [命令或者配置文件] 功能描述:获取帮助信息 例如:man ls 查看ls命令的帮助 ...
- 离乡与理想 Demo
离乡与理想---理想不分黑白 踏上了火车,离开家乡背着幻梦,站累了有人叫我幸福,叫我快乐而我是没有名字的流浪的日子,已数不清有多少带着理想,一路飞翔路上遇见困难,一定要坚强因为理想已离乡 我为理想而离 ...
- python:列表的方法
注意:在列表的类方法一般是没有返回值的,如果将处理过的列表给新变量,新变量是空类型.如:>>>a=[1,2]>>>b=a.append(3)>>> ...
- 修改jpivot源码实现分页
使用jpivot过程中,如果查询到的结果行数超过一个阈值,后面的显示就会丢失,这时需要分页显示. 假设应用中组装的MDX语句已经含有NON EMPTY,把空行直接过滤掉了. 这时需要修改的jpivot ...
- 安装配置Apollo-Prota web中心平台
首先要求2g以上内存哈,JDK1.8 搭建数据库 创建表 apollo-build-scripts-master 整个阿波罗环境包 使用一个命令启动整个阿波罗服务环境 创建两个数据库 分别为:apol ...
- nginx 反向代理配置之---可配置多域名请求
配置文件如下: server { listen 80; server_name ngin服务器所对应的的域名; error_log /data/logs/nginx/mainsite.error.lo ...
- LeetCode——Construct Binary Tree from Inorder and Postorder Traversal
Question Given inorder and postorder traversal of a tree, construct the binary tree. Note: You may a ...
- 域名解析中TTL是什么意思
在做域名解析的时候都会看到一个叫“TTL”的值,一般都有一个默认的值,不过不同注册商默认的值也会不一样,常见的是3600和7200这两个值. 另外ping的时候也可以看到“TTL=XXX”的字样,(如 ...
- vue.js计算属性 vs methods
计算属性:Vue.js 模板内的表达式非常便利,但是缺点就是只能用于简单的运算,如果模板中有太多的逻辑运算会让模板不堪重负且难以维护.恰恰计算属性可以处理复杂的逻辑运算,也就是说对于任何复杂逻辑你都应 ...
- UOJ14 DZY Loves Graph
DZY开始有 nn 个点,现在他对这 nn 个点进行了 mm 次操作,对于第 ii 个操作(从 11 开始编号)有可能的三种情况: Add a b: 表示在 aa 与 bb 之间连了一条长度为 ii ...