.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提供了众多的远程服务形式.在只 ...
随机推荐
- offsetLeft与style.left的区别
参考:http://www.cnblogs.com/woshilee/articles/1951457.html offsetLeft 获取的是相对于父对象的左边距 left 获取或设置相对于 具有定 ...
- 流量分析系统---kafka集群部署
1.集群部署的基本流程 Storm上游数据源之Kakfa 下载安装包.解压安装包.修改配置文件.分发安装包.启动集群 2.基础环境准备 安装前的准备工作(zk集群已经部署完毕) 关闭防火墙 chk ...
- 值得关注的10个Python语言学习博客
大家好,还记得我当时学习python的时候,我一直努力地寻找关于python的博客,但我发现它们的数量很少.这也是我建立这个博客的原因,向大家分享我自己学到的新知识.今天我向大家推荐10个值得我们关注 ...
- 安装好MySQL后就开始学习如何后台创建自己的数据库吧!
MySQL创建数据库的方式不像SQL Server那样有图形界面,而是使用DOS窗口创建的,接下来就是创建MySQL数据库的具体步骤. ↓↓↓↓↓↓↓↓ ↓↓↓↓↓↓↓↓ ↓↓↓↓ ...
- OpenCV图片拼接的两种方法
https://my.oschina.net/xiaot99/blog/226589 一.原图 1.jpg 2.jpg ...
- box-flex兼容写法
box-flex布局在这几年发生了多次变化,可分为2009版.2011版以及2013版, 区分: display:box(inline-box), box-{*}的格式为2009版 display:b ...
- php数组函数-array_map()
array_map()函数返回用户自定义函数作用后的数组.回调函数接受的参数 数目应该和传递给array_map()函数的数组数目一直. array_map(function,array1,array ...
- Java 内部类、静态类内部类
问: 什么是内部类? 答: 内部类(Inner Class)就是在一个类的内部再定义一个类,与之对应包含内部类的类被称为外部类. 问: 为什么要将一个类定义在另外一个类内部呢? 答: 内部类主要作用如 ...
- Kubernetes StatefulSets
StatefulSets对于需要以下一项或多项的应用程序非常有用. 稳定,唯一的网络标识符. 稳定,持久的存储. 有序,优雅的部署和缩放. 有序,优雅的删除和终止. 有序的自动滚动更新. POD Id ...
- Spring中为什么实体类不用注入
要理解为什么不用注入,首先就清楚注入的目的是什么?如果不注入,在程序中要使用某个类对象的方法,则需要去new一个对象.然后我们调用其中的方法,众所周知"程序=算法+数据".不失一般 ...