.Net Remoting(基本操作) - Part.2 (转)

Remoting 构架

1.客户端(客户应用程序)

客户端的处理包含三个基本的组成部分,代理(Proxy)、格式器(Formatter) 和 通道(Channel)。

 

2.服务端(宿主应用程序)

服务端主要由 通道(Channel)、格式器(Formatter)、Stack Builder组成。

在服务端,宿主程序保持着为Remoting所打开的端口的监听,一旦通道收到消息,它便将消息发送给Formatter,Formatter将消息进行反序列化,然后将消息发送给Stack Builder,Stack Builder读取消息,然后依据消息创建对象(可选),调用方法。方法返回时,Stack Builder将返回值封装为消息,然后再提交给Formatter,Formatter进行格式化之后,发送到通道传递消息。

 

3.Remoting对象的三种激活方式

上一章 .Net Remoting - Part.1 中,我们提到了传值封送和传引用封送,并各给出了一张示意图,实际上,传引用封送还分为了三种不同的方式,下面来一一来介绍。对于传引用封送,记住各种方式的共同点:服务对象创建且一直保持在宿主程序中

 

3.1客户激活(Client activated )

 

对于每个客户,创建了其专属的远程对象为其服务(由Part.1的代码可以看出,对象的状态在两次方法调用中也是维持着的)。除此以外,一个代理可以为多个客户对象服务。

 

客户激活模式的缺点就是 如果客户端过多时,或者服务对象为“大对象”时,服务器端的压力过大。另外,客户程序可能只需要调用服务对象的一个方法,但是却持有服务对象过长时间,这样浪费了服务器的资源。

 

3.2 服务激活 Singleton(Server activated Singleton)

这个模式的最大特色就是所有的客户共享同一个对象。服务端只在对象第一次被调用时创建服务对象,对于后继的访问使用同一个对象提供服务。

 

因为Sinlgton对象是在第一次访问(比如方法调用)时由.Net自动创建的,后继的访问不能重新创建对象,所以它不提供有参数的构造函数。另外,由于Singleton对象的状态由其它对象所共享,所以使用Singleton对象应该考虑线程同步 的问题。

3.3 服务激活 Single Call(Server activated Single Call)

Single Call方式是对每一次请求(比如方法调用)创建一个对象,而在每次方法返回之后销毁对象。由此可见Single Call 方式的最大特点就是 不保存状态。使用Single Call的好处就是不会过久地占用资源,因为方法返回后对资源的占用就随对象被销毁而释放了。最后,Single Call 方式也不允许使用由参数的构造函数。

Remoting程序的基本操作

 

1.服务程序集

我们首先创建服务程序集,它即为向客户程序提供服务的远程对象的实现代码。先创建一个类库项目ServerAssembly,然后创建类型ServerAssembly.DemoClass(为Part.1中的ClassLib.DemoClass添加了几个方法)。我们让它继承自MarshalByRefObject。

 

2.宿主应用程序

接下来我们新创建一个空解决方案ServerSide,在其下添加一个新的控制台项目ServerConsole,然后再将上面创建的项目ServerAssembly添加进来。除此以外,还需要添加System.Runtime.Remoting的引用。

 

2.1 注册通道

实现宿主应用程序的第一步就是注册通道。通道是实现了System.Runtime.Remoting.Channels.IChannel的类。通道分为两种,一种是发送请求的通道,比如说客户应用程序使用的通道,这种类型的通道还需要实现 System.Runtime.Remoting.Channels.IChannelSender 接口;一种是接收请求的通道,比如说宿主应用程序使用的通道,这种类型的通道还需实现System.Runtime.Remoting.Channels.IChannelReceiver接口。

通常我们不需要实现自己的通道,.Net 提供了三个内置的通道,分别是 System.Runtime.Remoting.Channels.Http.HttpChannel、

System.Runtime.Remoting.Channels.Tcp.TcpChannel

以及

System.Runtime.Remoting.Channels.Ipc.IpcChannel。

由于 IpcChannel 不能跨机器(只能跨进程),所以我们仅使用最为常用的 HttpChannel和TcpChannel为例作为说明。它们均实现了 System.Runtime.Remoting.Channels 命名空间下的 IChannel、IChannelSender、IChannelReceiver接口,所以它们既可以用于发送请求,也可以用于接收请求。

接下来需要对通道进行注册,然后对这个通道进行监听。对于同一个应用程序域,同一类型(实际上是同一名称,因为同一类型的通道默认名称相同)的通道只能注册一次。对同一机器来说,同一端口也只能使用一次。同一应用程序域可以注册多个不同类型的通道。注册的方式是调用ChannelServices类型的静态方法RegisterChannel();

 

当通道从端口监听到新请求时,它会从线程池中抓取一个线程执行请求,从而可以不间断地对端口进行监听(不会因为处理请求而中断)。当关闭宿主程序时,.Net会自动释放端口,以便其他程序可以使用该端口。

 

2.2 注册对象

注册通道之后,我们需要告诉.Net允许哪些类型可以被远程程序访问,这一步骤称为注册对象。如同上面所说的,有三种服务器端的远程对象类型:客户激活对象、服务激活Single Call、服务激活Singleton。

客户激活对象的注册方式需要使用RemotingConfiguration类型的RegisterActivatedServiceType()静态方法:

 

同一类型对象只可以用一种方式注册(客户激活 或者 服务激活)。即是说如果使用上面的方法注册对象,那么要么调用 ClientActivated(),要么调用ServerActivatedSingleCall()或者ServerActivatedSingleton(),而不能都调用。上面的RegisterWellKnownServiceType()方法接受三个参数:1.允许进行远程访问的对象类型信息;2.远程对象的名称,用于定位远程对象;3.服务激活对象的方式,Singleton或者Single Call。

 

2.3 对象位置

经过上面两步,我们已经开启了通道,并注册了对象(告诉了.Net哪个类型允许远程访问)。那么客户端如何知道远程对象位置呢?如同Web页面有一个Url一样,远程对象也有一个Url,这个Url提供了远程对象的位置。客户程序通过这个Url来获得远程对象。

RemotingConfiguration类型还有一个ApplicationName静态属性,当设置了这个属性之后,对于客户激活对象,可以提供此ApplicationName作为Url参数,也可以不提供。如果提供ApplicationName,则必须与服务端设置的ApplicationName相匹配;对于服务激活对象,访问时必须提供ApplicationName,此时两种方式的Uri为下面的形式:

protocal://hostadrress:port/ApplicationName/ObjectUrl       // Server Activated
protocal://hostadrress:port                     // Client Activated Object
protocal:// hostadrress:port/ApplicationName    // Client Activated Object

比如,如果通道采用协议为tcp,服务器地址为127.0.0.1,端口号为8051,ApplicationName设为DemoApp,ObjectUrl设为RemoteObject(ObjUrl为使用RegisterWellKnownServiceType()方法注册服务激活对象时第2个参数所提供的字符串;注意客户激活对象不使用它),则客户端在访问时需要提供的地址为:

tcp://127.0.0.1:8051/DemoApp/RemoteObject   // Server Activated Object
tcp://127.0.0.1:8051/DemoApp                // Client Activated Object
tcp://127.0.0.1:8051                        // Client Activated Object

如果RemotingConfiguration类型没有设置ApplicationName静态属性,则客户端在获取远程对象时不需要提供ApplicationName,此时Url变为下面形式:

protocal://hostadrress:port/ObjectUrl       // Server Activated Object
protocal://hostadrress:port                 // Client Activated Object

 

3.客户应用程序

我们现在再创建一个空解决方案ClientSide,然后在其下添加一个控制台应用程序ClientConsole,因为客户端需要ServerAssembly.DemoClass的元信息来创建代理,所以我们仍要添加对ServerAssembly项目的引用。除此以外,我们依然要添加 System.Runtime.Remoting程序集。

客户应用程序的任务只有一个:获取远程对象,调用远程对象服务。记得客户应用程序实际上获得的只是一个代理,只是感觉上和远程对象一模一样。客户端获得对象有大致下面几种情况:

3.1使用new操作符创建远程对象

客户应用程序可以直接使用new获得一个远程对象。例如下面语句:

DemoClass obj = new DemoClass();

看到这里你可能很惊讶,这样的话不是和通常的创建对象没有区别,为什么创建的是远程对象(这里用“远程对象”,只是为了说明方便,要记得实际上是代理对象)而非本地对象呢(注意本地客户程序ClientConsole也引用了ServerAssembly项目)?其实.Net和你一样,它也不知道这里要创建的是远程对象,所以,在使用new创建远程对象之前,我们首先要注册对象。注册对象的目的是告诉.Net,这个类型的对象将在远程创建,同时还要告诉.Net远程对象的位置。

我们知道远程对象有 客户激活 和 服务激活 两种可能,因此客户程序注册也分为了两种情况 -- 注册客户激活对象,注册服务激活对象。在客户端注册对象也是通过RemotingConfiguration类型来完成:

// 注册客户激活对象
private static void ClientActivated() {
    Type t = typeof(DemoClass);

    // 下面两个 url 任选一个
    string url = "tcp://127.0.0.1:8501";   
    //string url = "tcp://127.0.0.1:8501/SimpleRemote";
    RemotingConfiguration.RegisterActivatedClientType(t, url);
}

// 注册服务激活对象
private static void ServerActivated() {
    Type t = typeof(DemoClass);
    string url = "tcp://127.0.0.1:8501/SimpleRemote/ServerActivated";
    RemotingConfiguration.RegisterWellKnownClientType(t, url);
}

我们看到,尽管在服务端,服务激活有两种可能的方式,Singleton和SingleCall,但是在客户端,服务激活的两种方式采用同一个方法RegisterWellKnownClientType()方法进行注册。所以我们可以说 服务端决定服务激活对象的运行方式(Singleton或SingleCall)。

 

3.2 其它创建远程对象的方法

当我们在客户端对远程对象进行注册之后,可以直接使用new操作符创建对象。如果不进行注册来创建远程对象,可以通过 RemotingServices.Connect()、Activator.GetObject()、Activator.CreateInstance()方法来完成:

string url = "tcp://127.0.0.1:8501/SimpleRemote/ServerActivated";
// 方式1
DemoClass obj = (DemoClass)RemotingServices.Connect(typeof(DemoClass), url);
// 方式2
DemoClass obj = (DemoClass)Activator.GetObject(typeof(DemoClass), url);
// 方式3
object[] activationAtt = { new UrlAttribute(url) };
DemoClass obj = (DemoClass)Activator.CreateInstance(typeof(DemoClass), null, activationAtt);

这几种方法,RemotingServices.Connect()和Activator.GetObject()是最简单也较为常用的,它们都是只能创建服务激活对象,且创建的对象只能有无参数的构造函数,并且在获得对象的同时创建代理。Activator.CreateInstance()提供了多达13个重载方法,允许创建客户激活对象,也允许使用有参数的构造函数创建对象,并且可以先返回一个Wrapper(包装)状态的对象,然后在以后需要的时候通过UnWrap()方法创建代理。CreateInstance()方法更详细的内容可以参考MSDN。

 

4.程序运行测试

Remoting 最让初学者感到困惑的一个方面就是 客户激活 与 服务激活 有什么不同,什么时候应该使用那种方式。说明它们之间的不同的最好方式就是通过下面几个范例来说明,现在我们来将上面的服务端方法、客户端方法分别进行一下组装,然后进行一下测试(注意在运行客户端之前必须保证服务端已经运行):

4.1 客户激活方式

先看下客户激活方式,服务端的Main()代码如下:

static void Main(string[] args) {
    RegisterChannel();          // 注册通道
    ClientActivated();          // 客户激活方式

    Console.WriteLine("服务开启,可按任意键退出... ");
    Console.ReadKey();
}

客户端的Main()代码如下:

static void Main(string[] args) {
    // 注册远程对象
    ClientActivated();      // 客户激活方式

    RunTest("Jimmy", "Zhang");
    RunTest("Bruce", "Wang");

    Console.WriteLine("客户端运行结束,按任意键退出...");
    Console.ReadKey();
}

private static void RunTest(string firstName, string familyName) {
    DemoClass obj = new DemoClass();
    obj.ShowAppDomain();
    obj.ShowCount(firstName);
    Console.WriteLine("{0}, the count is {1}. ",firstName, obj.GetCount());
               
    obj.ShowCount(familyName);
    Console.WriteLine("{0}, the count is {1}.",familyName, obj.GetCount());
}

程序运行的结果如下:

 

程序运行的结果如下:

其中第一幅图是服务端,第二幅图是客户端,我们起码可以得出下面几个结论:

  1. 不管是对象的创建,还是对象方法的执行,都在服务端(远程)执行。
  2. 服务端为每一个客户端(两次RunTest()调用,各创建了一个对象)创建其专属的对象,为这个客户提供服务,并且保存状态(第二次调用ShowCount()的值基于第一次调用ShowCount()之后count的值)。
  3. 可以从远程获取到方法执行的返回值。(客户端从GetCount()方法获得了返回值)

上面的第3点看起来好像是理所当然的,如果是调用本地对象的方法,那么确实是显而易见的。但是对于远程来说,就存在一个很大的问题:远程对象如何知道是谁在调用它?方法执行完毕,将返回值发送给哪个客户呢?此时可以回顾一下第一篇所提到的,客户端在创建远程对象时,已经将自己的位置通过消息发送给了远程。

最后我们再进行一个深入测试,追踪对象是在调用new时创建,还是在方法调用时创建。将RunTest()只保留一行代码:

private static void RunTest(string firstName, string familyName) {
    DemoClass obj = new DemoClass();    // 创建对象
}

然后再次运行程序,得到的输出分别如下:

 

 

// 服务端
方式: Client Activated Object
服务端开启,按任意键退出...
======= DomoClass Constructor =======
======= DomoClass Constructor =======

// 客户端
客户端运行结束,按任意键退出...

由此可以得出结论:使用客户激活方式时,远程对象在调用new操作时创建。

 

 

4.2 服务激活方式 -- Singleton

我们再来看一下服务激活的Singleton方式。先看服务端代码(“按任意键退出”等提示语句均以省略,下同):

static void Main(string[] args) {
    RegisterChannel();              // 注册通道
    ServerActivatedSingleton();     // Singleton方式
}

再看下客户端的Main()方法:

static void Main(string[] args) {
    // 注册远程对象
    ServerActivated();     

    RunTest("Jimmy", "Zhang");
    RunTest("Bruce", "Wang");
}

程序的运行结果如下:

同上面一样,第一幅为服务端,第二幅图为客户端。从图中我们可以得出:当使用Singleton模式时,服务端在第一次请求时创建一个对象(构造函数只调用了一次)。对于后继的请求仅使用这个对象进行服务(即使再次调用构造函数也不会创建对象),同时多个客户端共享同一个对象的状态(ShowCount()的值累加)。

我们和上一小节一样,再次将客户端的RunTest()只保留为“DemoClass obj = new DemoClass(); ”一行语句,然后运行程序,得到的结果为:

// 服务端
方式: Server Activated Singleton
服务端开启,按任意键退出...
// 客户端
客户端运行结束,按任意键退出...

这个结果出乎我们意料,但它又向我们揭示了Singleton的另一个性质:即使使用new操作符,客户端也无法创建一个对象,而只有在对象上第一次调用方法时才会创建。仔细考虑一下这个和上面的结论是类似的,只是更深入了一步。

4.3 服务激活方式 -- SingleCall

最后我们看一下SingleCall方式,注意到客户端的代码不需要做任何修改,所以我们只需要切换一下服务端的激活方式就可以了:

static void Main(string[] args) {
    RegisterChannel();          // 注册通道
    ServerActivatedSingleCall();
}

我们再次看一下运行结果:

我们可能首先惊讶构造函数居然调用了有10次之多,在每次RunTest()方法中各调用了5次。如同前面所说,对于SingleCall方式来说,对象对每一次方法调用提供服务,换言之,对于每一次方法调用,创建一个全新的对象为其服务,在方法执行完毕后销毁对象。我们再看下客户端的输出:GetCount()方法全部返回0,现在也很明确了,因为每次方法调用都会创建新对象(在创建对象时,int类型的count被赋默认值0),所以SingleCall方式是不会保存对象状态的。如果想要为对象保存状态,那么需要另外的机制,比如将状态存储到对象之外:

public void ShowCount(string name, object clientId) {
    LoadStatus(this, clientId);         // 加载对象状态
    count++;
    Console.WriteLine("{0},the count is {1}.", name, count);
    SaveStatus(this, clientId);         // 存储对象状态
}

其中LoadStatus()、SaveStatus()方法分别用于加载对象状态和 存储对象状态。注意到ShowCount()方法多了一个clientId参数,这个参数用于标示客户程序的id,因为服务端需要知道当前是为哪个客户程序加载状态。

最后,我们再次进行一下上面两节将RunTest()只保留为创建对象的一行代码,得到的运行结果和Singleton是一样的:

// 服务端
方式: Server Activated Singleton
服务端开启,按任意键退出...
// 客户端
客户端运行结束,按任意键退出...

这说明使用SingleCall时,即使使用了new 来创建对象,也不会调用构造函数,只有在调用方法时才会创建对象(调用了构造函数)。

Remoting中的传值封送

很多朋友可能此刻会感到些许困惑,在Part.1的范例中,我们讲述AppDomain时,使用了传值封送和传引用封送两种方式,但是上面的三种激活方式都属于传引用封送。那么如何进行对象的传值封送呢(将DemoClass直接传到本地)?实际上,在上面的例子中,我们已经进行了传值封送,这个过程发生在我们在客户端调用 GetCount() 时。为什么呢?想一想,count值本来是位于服务端的,且int为可序列化对象(Serializable),在向客户端返回方法结果时,count值被包装为了消息,然后由服务端发送回了客户端,最后在客户端进行了解包装及还原状态。

为了看得更清楚一些,我们在ServerAssembly中再创建一个DemoCount类型,然后对这个类型进行传值封送,因为DemoCount仅仅是为了传送数据,不包含任何行为,所以我们将它声明为结构:

public class DemoClass : MarshalByRefObject {
    // 其余方法略...

    // 示范传值封送
    public DemoCount GetNewCount() {
        return new DemoCount(count);
    }
}

[Serializable]
public struct DemoCount {
    private readonly int count;
    public DemoCount(int count) {
        this.count = count;
    }
    public int Count {
        get { return count; }
    }
    public void ShowAppDomain() {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        Console.WriteLine(currentDomain.FriendlyName);
    }
}

在DemoClass中,我们又添加一个方法,它根据count的值创建了DemoCount对象,而DemoCount对象会通过传值封送传递到客户端。

现在修改客户端,再重载一个RunTest()方法,用来测试这次的传值封送:

// 测试传值封送
private static void RunTest() {
    DemoClass obj = new DemoClass();
    obj.ShowAppDomain();                // 显示远程对象所在应用程序域
    obj.ShowCount("张子阳");     // Count = 1

    DemoCount myCount = obj.GetNewCount();   // 传值封送DemoCount
    myCount.ShowAppDomain();        // 显示DemoCount所在应用程序域

    // 在客户端显示count值
    Console.WriteLine("张子阳, count: {0}.", myCount.Count);
}

此时我们再次进行测试,得到的结果如下:

可以看到,我们在客户端DemoCount上调用ShowAppDomain()方法时,返回了ClientApp.exe,可见DemoCount已经通过传值封送传递到了客户端。那么我们继续上面的问题,如何将DemoClass整个传值封送过来呢?首先,我认为没有这个必要,如果将服务对象整个封送到客户端来执行,那么Remoting还有什么意义呢?其次,我们来看如何实现它。方法很简单,我们创建一个工厂类作为远程服务对象,然后将我们实际要传值封送到客户端的对象(比如DemoClass),作为工厂方法的返回值。这个例子我就不再演示了,相信看过上面的示例,您已经明白了。

 

转载于:https://www.cnblogs.com/MayGarden/archive/2010/06/21/1762195.html

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

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

相关文章

转载——CVE-2019-0807

译文声明 本文是翻译文章,文章原作者mcafee,文章来源:securingtomorrow.mcafee.com 原文地址:https://securingtomorrow.mcafee.com/other-blogs/mcafee-labs/rdp-stands-for-really-do-patch-understanding-the-wormable-rdp-v…

angular移除事件绑定事件绑定_Vue.js子组件利用事件向父组件传输数据,以及sync修饰符和双向绑定...

在Vue.js的组件中,prop是“单向绑定”的,数据只能从父组件传输到子组件。Vue文档中的说了这样做的原因:所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则…

1!+2!+3!+…+10!的和

1!2!3!…10!的和 代码 public class Test {public static void main(String[] args) {/*输出1&#xff01;2&#xff01;***10&#xff01;的和*/int sum0;int n1;for (int i 1; i <10 ; i) {for (int j1;j<i;j){nn*j;}sumn;n1;}System.out.println("sum"su…

ipad无法充电怎么办_IPAD充电线破损无法保修,资深“果粉”吐槽:店大欺客!...

应该有不少人都是在使用苹果的电子产品吧&#xff1f;包括iphone、ipad之类的。您在使用的过程中有没有发现一个问题&#xff0c;这些电子产品的充电线似乎很不经用&#xff0c;没多久就会发生断裂或者破损&#xff0c;但大部分人都很少会去较真&#xff0c;一般都是自己花钱再…

有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

代码 import java.util.Scanner;public class Testified {public static void main(String[] args) {Scanner scanner new Scanner(System.in);System.out.println("请输入月数:");int n scanner.nextInt();System.out.println("第"n"个月的兔子总…

jbod ugood 磁盘驱动状态_如何检查Mac磁盘空间,mac磁盘空间其他怎么清理

致力于成为您终身的苹果管家点击上方蓝字 关注我们检查Mac磁盘空间的时候&#xff0c;你会发现“其他存储”占用了驱动器太多的空间。你知道Mac上的其他存储在哪里&#xff0c;mac磁盘空间其他怎么清理吗&#xff1f;这里macw小编为大家带来了详细的mac 清理磁盘空间教程&…

最强鸿蒙系统txt_鸿蒙系统升级时间确认,哪些手机有望成为首批“宠儿”?

在华为麒麟芯片受到打压的这段时间&#xff0c;鸿蒙系统可以说是华为近阶段最受瞩目的研发项目之一。鸿蒙系统不仅仅是一个手机操作系统&#xff0c;它还可以安装在PC、智能家居设备。它的强大除了体现在很多方面。例如传统APP开发代码复杂&#xff0c;劳动强度大&#xff0c;后…

关于Tomcat双击startup.bat文件一闪而过问题

原因&#xff1a; 这是因为我们没有配置JAVA_HOME环境变量&#xff0c;Tomcat本身就是java写的解决方法 找到我们电脑的属性&#xff0c;打开&#xff0c;显示以下界面&#xff1a; 点击高级系统设置&#xff0c;进入下面的界面&#xff1a; 点击环境变量吗&#xff0c;进…

windows内核情景分析---进程线程2

二、线程调度与切换 众所周知&#xff1a;Windows系统是一个分时抢占式系统&#xff0c;分时指每个线程分配时间片&#xff0c;抢占指时间片到期前&#xff0c;中途可以被其他更高优先级的线程强制抢占。 背景知识&#xff1a;每个cpu都有一个TSS&#xff0c;叫‘任务状态段’…

asp.net mvc 2 DisplayTemplates 的使用

asp.net mvc 2 官方给的例子有些简单&#xff0c;主要介绍了新的功能。下面主要介绍下DisplayTemplates 给我们带来的实用的功能&#xff0c;可以自定义字段显示的方式&#xff0c;按类型分&#xff1a;String、Boolean、Decimal。按显示的方式&#xff1a;EmailAddress、Html、…

关于Tomcat端口8080占用问题(解决方法)

解决方法 打开cmd命令提示符&#xff0c;输入netstat -aov&#xff0c;在本地地址下找到8080端口&#xff0c;显示如下&#xff1a; 这个时候我们要记得PID后面的数字&#xff0c;我的是8740&#xff1a; 然后打开任务管理器&#xff0c;在详细信息下找的PID为8740的&#…

eclipse和Tomcat绑定

在file下new一个Dynamic Web Project 取好名字&#xff0c;点击next 或者finish都可以&#xff0c;完成创建 点击window,选择preferences 在搜索框里输入server,然后点击Runtime Environments 点击add 我的版本是7&#xff0c;选择7&#xff0c;大家是那个版本就选择…

OD里alt+F9和Ctrl+F9和shift+F9的区别

ShiftF9 - 与F9相同&#xff0c;但是如果被调试程序发生异常而中止&#xff0c;调试器会首先尝试执行被调试程序指定的异常处理&#xff08;请参考忽略Kernel32中的内存非法访问&#xff09;。 CtrlF9 - 执行直到返回&#xff0c;跟踪程序直到遇到返回&#xff0c;在此期间不进…

kafka数据不丢失不重复_超高速底层系统数据复制,安全精准不丢失

大家好&#xff0c;我是IT数码手机控&#xff0c;一名究极数码爱好者&#xff0c;喜欢最潮最酷的数码好物&#xff0c;追求科技美学&#xff0c;数码圈里最懂艺术&#xff0c;艺术圈里最会修电脑的女汉纸~而今天就给大家介绍佑华硬盘数据拷贝机。— ❶ —外观质感拿到佑华 PRO-…

如何在Visual Studio项目中正确添加汇编代码 .

引用注明>> 【作者&#xff1a;张佩】【镜像&#xff1a;www.yiiyee.cn/blog】 1. 问题描述 在以往的编程经历中&#xff0c;本人最常使用的汇编代码是__asm {int 3}。它可以在我的代码中插入一个软件断点。如果没有一个连接到当前程序的调试器&#xff0c;则程序将…

多叉树的前序遍历_二叉树的非递归遍历的思考

封面图来自wikipedia1 简介二叉树的深度优先遍历&#xff08;前序遍历、中序遍历、后序遍历&#xff09;是一个比较基本的操作。如果使用递归的做法&#xff0c;很容易写出相应的程序&#xff1b;而如果使用非递归的做法&#xff0c;虽然也能写出相应的代码&#xff0c;但是由于…

delphi中Label中文显示不全的问题解决办法

有时候把Label的AutoSize属性设置为True&#xff0c;当窗体显示的时候&#xff0c;Label中的内容可能会显示不完全&#xff0c;只能把AutoSize设置为False&#xff0c; 把Label调整成能显示出内容的大小。还有一种更简单的解决方法。把Form的Font属性进行如下设置&#xff1a;字…

焊接空间臂_焊接烟尘净化器设备哪种好

焊接烟尘净化器设备采用滤筒除尘器&#xff0c;焊接烟尘净化器用于焊接、切割、打磨等工序中产生烟尘和粉尘的净化以及对稀有金属、贵重物料的回收等&#xff0c;可净化大量悬浮在空气中对人体有害的细小金属颗粒。具有净化效率高、噪声低、使用灵活、占地面积小等特点。 适用于…

【摘录】C语言中利用 strtok函数进行字符串分割

C语言不像Java,Php之类的高级语言&#xff0c;对象中直接封装了字符串的处理函数。C语言中进行普通的字符串处理也经常会让我们焦头烂额……不过好在C语言 中还是提供了像strtok这样功能强大的字符串处理函数&#xff0c;可以帮我们实现部分需要的功能。下面我们介绍一下strtok…

woe分析_Python数据分析—apply函数

在对海量数据进行分析的过程中&#xff0c;我们可能要把文本型的数据处理成数值型的数据&#xff0c;方便放到模型中进行使用。也可能需要把数值型的数据分段进行处理&#xff0c;比如变量的woe化。而这些操作都可以借助python中的apply函数进行处理。今天介绍数据分析的第四课…