netcore实践:跨平台动态加载native组件

缘起netcore框架下实现基于zmq的应用。在.net framework时代,我们进行zmq开发由很多的选择,比较常用的有clrzmq4和NetMQ。 其中clrzmq是基于libzmq的Interop包装,

NetMQ是100%C#的zmq实现(基于AsyncIO组件)。以上两种组件我都有过应用,孰优孰劣各有千秋,本文就不详谈了。

 

回归正题,netcore下使用zmq首先也是想到引用上述组件,实践后发现clrzmq暂时没有基于netstandard或者netcore的支持,而NetMQ做的比较好,已经基于netstandard1.3进行了支持。

一番折腾,搭程序,配环境。。。而后 dotnet run ,在windows下正常呈现了zmq的各项功能。

 

于是继续dotnet publlish,通过Wnscp拷贝到centos7下执行。立即报错,

挺意外的,本以为NetMQ已经基于netstandard进行了支持,也应该对跨平台进行支持。 可事实是AsyncIO的IOControl方法not supported on linux platform/

 

无奈,网上搜了会,也没找到任何关于netcore在linux下进行zmq的相关内容, 事实上也没有看到AsyncIO或者NetMQ有关于跨平台支持的说明。

既然现有方式行不通那就只好自己动手了,自己操刀通过Interop包装libzmq来实现跨平台的zmq应用吧!

 

首先看下libzmq的组件目录: 按x86和x64平台分为i386文件夹和amd64文件夹,且都包含windos下的dll组件和linux下的so组件

这挺难办了,要想做好还得考虑平台类型 和 操作系统类型,  还要想想 netcore里有没有相关API提供。 

 

先是网上搜了圈,也极少有关于netcore进行平台判断相关内容。根据以往在framework下的经验,直接到https://apisof.net搜索相关关键字

:OSPlatform

非常不错,netcore已经提供了,同理搜索了 OSArchitecture 、DllImport 等都发现netcore1.1版本已经实现了相关api (说白了是netstandard已经实现了相关API)

 

准备就绪,直接开干,首先是平台相关信息判断,并加载目标组件 (具体方式请参照如下代码)

class ZmqNative

    {

        private const string LibraryName = "libzmq";


        const int RTLD_NOW = 2; // for dlopen's flags

        const int RTLD_GLOBAL = 8;

        [DllImport(@"libdl")]

        static extern IntPtr dlopen(string filename, int flags);

        [DllImport("libdl")]

        protected static extern IntPtr dlsym(IntPtr handle, string symbol);



        [DllImport("kernel32.dll")]

        static extern IntPtr LoadLibrary(string filename);


        private static IntPtr LibPtr = IntPtr.Zero;

        static ZmqNative()

        {

           

            Console.WriteLine("OSArchitecture:{0}",RuntimeInformation.OSArchitecture);

            try {

                var libPath = @"i386";

                if (RuntimeInformation.OSArchitecture == Architecture.X86)

                {

                    libPath = @"i386";

                }

                else if (RuntimeInformation.OSArchitecture == Architecture.X64)

                {

                    libPath = @"amd64";

                }

                else

                {

                    Console.WriteLine("OSArchitecture not suported!");

                }


                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))

                {

                    var libName = $"{AppContext.BaseDirectory}\\{libPath}\\{LibraryName}.dll";

                    Console.WriteLine("windows:{0}", libName);

                    LibPtr = LoadLibrary(libName);

                   

                }

                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))

                {

                    var libName = $"{AppContext.BaseDirectory}/{libPath}/{LibraryName}.so";

                    Console.WriteLine("linux:{0}", libName);

                    LibPtr = dlopen(libName, RTLD_NOW|RTLD_GLOBAL);


                    

                    if(LibPtr!=IntPtr.Zero)

                    {

                        var ptr1 = dlsym(LibPtr, "zmq_ctx_new");

                        context = Marshal.GetDelegateForFunctionPointer<ZmqContext>(ptr1) ;


                        var ptr2 = dlsym(LibPtr, "zmq_socket");

                        socket = Marshal.GetDelegateForFunctionPointer<ZmqSocket>(ptr2);


                        var ptr3 = dlsym(LibPtr, "zmq_connect");

                        connect = Marshal.GetDelegateForFunctionPointer<ZmqConnect>(ptr3);


                    }

    }

                else

                {

                    Console.WriteLine("OSPlatform not suported!");

                }

                if (LibPtr != IntPtr.Zero)

                    Console.WriteLine("load zmqlib success!");

            }

            catch(Exception ex)

            {

                Console.WriteLine("load zmqlib error:\r\n{0}",ex);

            }

        }


        public delegate IntPtr ZmqContext();


        [DllImport(LibraryName, EntryPoint = "zmq_ctx_new", CallingConvention=CallingConvention.Cdecl)]

        public static extern IntPtr zmq_ctx_new();

        public static ZmqContext context = null;



        public delegate IntPtr ZmqSocket(IntPtr context, Int32 type);

        [DllImport(LibraryName, EntryPoint = "zmq_socket", CallingConvention = CallingConvention.Cdecl)]

        public static extern IntPtr zmq_socket(IntPtr context, Int32 type);

        public static ZmqSocket socket = null;



        public delegate Int32 ZmqConnect(IntPtr socket, IntPtr endpoint);

        [DllImport(LibraryName, EntryPoint = "zmq_connect", CallingConvention = CallingConvention.Cdecl)]

        public static extern Int32 zmq_connect(IntPtr socket, IntPtr endpoint);

        public static ZmqConnect connect = null;



        [DllImport(LibraryName, EntryPoint = "zmq_errno", CallingConvention = CallingConvention.Cdecl)]

        public static extern Int32 zmq_errno();


        [DllImport(LibraryName, EntryPoint = "zmq_strerror", CallingConvention = CallingConvention.Cdecl)]

        public static extern IntPtr zmq_strerror(int errnum);

    }

以上为测试代码,请自动忽略代码质量!

 

简单解释下,如上代码通过平台判断,动态加载组件,采用LoadLibaray的方式。 有心的同学可能会发现几个delegate并且在Linux部分内通过dlsym获取了函数指针,具体原因下面会讲。

 

以上测试代码,在windows平台下同样正常无误, 而在linux下还是遇到几个小坑~~容我慢慢道来:

1、通过DllImport进行Interop的时候,组件路径必须是确定的,这就引起了如何动态加载不同目录下组件的问题;

    好在windows平台下通过LoadLibaray加载dll到进程空间后,DllImport标记的函数就从进程空间查找,不会重复import组件了。

   而同样的原理在linux下用dlopen却不能实现,还是会提示找不到组件

 

2、初次部署centos7上时,报找不到libdl.so组件问题,主要原因是系统下没有glibc的原因,该问题可以通过yum安装glibc的方式解决;

 

//先查找系统内是否存在组件$ sudo find / -name libdl*//如不存在则安装glibc#
yum install glibc#安装完毕后进行链接 $ sudo ln -s /usr/lib64/libdl.so.2 /usr/lib64/libdl


3、解决了libdl组件问题后,继续运行还是会发现报找不到libzmq组件的问题,实际就产生了问题1中所描述的,在linux系统下dlopen后,Interop过的函数并不会从进程空间查找。 

 

为了解决上面遇到的问题,我们还有一条办法,就是创建 delegate , 并且通过LoadLibaray组件后通过GetProcAddress方式获取函数指针了。  具体的解决方案在上述测试代码已经体现了,这里就不过多解释了。

 

以上,就全部解决了在 netcore框架基础上进行跨平台native组件应用的问题。 真实测试结果如图:

 

请主动忽略初zmq应用外的其他信息, 本次测试一同测试了通过App入口启动webapi +  websockets + zmq ,api创建为aspnetcore在Web.dll内,websockets在Lib.dll内,zmq在App.dll内。

原文地址:http://www.cnblogs.com/cxwx/p/6726441.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325911.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何实现酷狗音乐pc页面点击播放时,打开多个歌曲播放时,始终在一个播放页面,(标签页的通讯)

大致有两种思路&#xff0c; 一种是通过wind.open()方法传第二个参数&#xff0c; A页面&#xff1a; //点击跳转播放页函数function toPlayPage(){window.open(path/xxxx/xxxx?name音乐名,music)//第二个参数写一个定值&#xff0c;代表跳转页面都为music标签页&#xff0…

Redis进阶之内存模型

转载自 Redis进阶之内存模型 前言 Redis是目前最火爆的内存数据库之一&#xff0c;通过在内存中读写数据&#xff0c;大大提高了读写速度&#xff0c;可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时&#xff0c;会接触Redis的5种对象类型&#xff08;字符…

C#将引入可空的引用类型

是的&#xff0c;标题没错。C#其中一份新提案假定&#xff0c;所有的引用类型在默认情况下都是不可空的。在新语法下&#xff0c;你需要显式地标明一个引用变量是可空的&#xff0c;就像对值类型所做的那样。 和值类型一样&#xff0c;T是指不可空类型&#xff0c;而T?是指可…

Android中SlidingDrawer开发报错You need to use a Theme.AppCompat theme (or descendant) with this activity.

Android抽屉开发报错You need to use a Theme.AppCompat theme (or descendant) with this activity. 方法1&#xff1a; 创建的activity时&#xff0c;如果不是那么强烈需要继承自AppCompatActivity&#xff0c;就直接继承Activity。 如将activity继承自AppCompatActivity&…

Redis进阶之持久化

转载自 Redis进阶之持久化 一、Redis高可用概述 在介绍Redis高可用之前&#xff0c;先说明一下在Redis的语境中高可用的含义。 我们知道&#xff0c;在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&am…

未来的.NET之多重继承

通过抽象接口引入有限形式的多重继承&#xff0c;这一.NET新提议颇具争议性。该特性是受Java默认方法&#xff08;Default Methods&#xff09;的启发。 默认方法的目的在于允许开发人员修改已发布的抽象接口。修改已发布接口将会产生破坏性的更改&#xff0c;因此在Java和.NE…

React中的方法调用

onClick{demo}//可以调用函数 onClick{demo()}//返回的是undefide

企业级负载均衡如何实现

转载自 企业级负载均衡如何实现 负载均衡简介 首先&#xff0c;我们来了解一下什么是负载均衡&#xff1a; 在一个大型网站中&#xff0c;在线用户有时可能有几千个甚至上万个之多。如果一个用户的请求需要服务使用0.02秒来处理&#xff0c;那么该服务实例每秒钟将只能处理…

微服务架构师的职责——《微服务设计读书笔记》

如何定义架构师 架构师从英文单词Architect翻译而来&#xff0c;在英文中&#xff0c;Architect原来的意思是“建筑师”。作者吐槽英文中架构师与传统的建筑师单词相同&#xff0c;但实际的工作性质并不相同&#xff0c;以致于在英文的语境中会造成理解上的差异。 传统的建筑师…

Android RaingBar评分条的使用

概述 RatingBar是基于SeekBar和ProgressBar的扩展&#xff0c;用星型来显示等级评定。使用RatingBar的默认大小时&#xff0c;用户可以触摸/拖动或使用键来设置评分&#xff0c;它有两种样式(小风格用ratingBarStyleSmall&#xff0c;大风格用ratingBarStyleIndicator)&#x…

理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用

一. 为什么是它们三个? 拿这三者比较的原因是它们在使用的时候非常相似。你可以用它们声明的变量赋任何类型的值。 看看下面的示例: var a 1;object b 1; dynamic c 1; 你还可以使用关键字为它们赋上更加复杂的类型 var a new string[]{"1"};object b new …

获取笔记本的SHA1的值。

开发Android几年来&#xff0c;经常出现这样的情况&#xff0c;每次使用到地图的时候&#xff0c;不知道如何获得笔记本的SHA1值&#xff0c;尤其是在跟换笔记本开发的时候。 因此在CSDN上做一下笔记&#xff01; 进入cmd模式 cd C:\Users\Administrator.android> 输入keyto…

建模:确定服务的边界——《微服务设计》读书笔记

什么样的服务才是好的服务? 高内聚、松耦合的服务才是好的服务。简而言之&#xff0c;就是把相关性强的放在一起&#xff0c;相关性不强的分开&#xff0c;物以类聚&#xff0c;人以群分&#xff0c;服务的划分也是这样。这就需要确定什么要放在一起&#xff0c;什么是要分开的…

谈谈系统稳定性设计

转载自 谈谈系统稳定性设计 一、差旅随想 因为base在分公司&#xff0c;需要经常去总部出差&#xff0c;所以搭乘飞机成了家常便饭&#xff0c;很多时候坐在飞机上会不由的感叹&#xff0c;设计制造这样精密复杂的机器的那帮人真的是了不起&#xff0c;他们是怎样保证这样一台…

Android使用MPAndroidChat

参考文档&#xff1a; https://blog.csdn.net/u013184970/article/details/52095170 https://blog.csdn.net/cen_yuan/article/details/52204281 注意&#xff1a; 在要使用的module的build.gradle添加: dependencies { compile ‘com.github.PhilJay:MPAndroidChart:v3.0.0-be…

微服务集成——《微服务设计》读书笔记

一.理想的集成应该是什么样的&#xff1f; 1.避免破坏性修改 如果在一个微服务的响应中添加一个字段&#xff0c;服务的消费方不应该受到影响。 2.保证API的技术无关性 微服务之间的通信应该是与技术无关的。 3.使服务的消费方易于使用 如果消费方使用该服务比登天还难&…

什么是加密算法

转载自 什么是加密算法 Java的加密知识也是Java常见的领域之一&#xff0c;加密技术的底层确实很复杂&#xff0c;运用了大量的数学知识&#xff0c;要弄明白非常复杂。但是Java语言中运用密码加密工具却是非常简单。我们在Java里面运用这些加密技术&#xff0c;只需要把原理…

Android传感器的使用

Android 中主要的传感器有以下几种 TYPE_ACCELEROMETER 加速度传感器又叫 G-sensor&#xff0c;该数值包含地心引力的影响&#xff0c;单位是 m/s 测量应用于设备 x 、y、z 轴上的加速度。 将手机平放在桌面上&#xff0c;x 轴默认为0&#xff0c;y 轴默认0&#xff0c;z 轴默…

理解C# 4 dynamic(2) – ExpandoObject的使用

ExpandoObject的使用非常简单&#xff0c;很容易入手。上一篇里面已经有详细的介绍了&#xff0c;可以看这里(理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用) 下面对ExpandoObject的使用场合和一些认为需要注意的地方&#xff0c;谈一下自己的看法: 一…