One of the Microsoft. NET Remoting Series tutorials:. NET Remoting Basics _ self-study process

Source: Internet
Author: User
Tags arrays constructor serialization

First, remoting Foundation

What is remoting, in short, we can look at it as a distributed approach. From the perspective of Microsoft's products, it can be said that remoting is an upgrade of DCOM, it improved a lot of functions, and excellent integration to. NET platform. Microsoft. NET Remoting provides a framework that allows an object to interact with another object through an application domain. That's why we use Remoting. Why, then? In the Windows operating system, the application is separated into a separate process. This process forms a boundary around the application code and data. If the interprocess communication (RPC) mechanism is not used, code executed in one process cannot access another process. This is an operating system protection mechanism for the application. In some cases, however, we need to cross the application domain and communicate with another application domain, that is, across boundaries.

In remoting, the communication of objects between two application domains is achieved through a channel (channel). As shown in the figure:

First, the client passes through the remoting, accesses the channel to obtain the server object, and then resolves the client object through the proxy. This provides the possibility of publishing server objects in a service manner. Remote object code can run on the server (such as server-activated objects and client-activated objects), then the client then connects to the server via remoting, obtains the service object, and runs it through serialization on the client.

In remoting, for objects to be passed, the designer does not need to know the format of the packet in addition to the type and port number of the channel. However, it is important to note that when the client obtains the server-side object, it does not get the actual service-side object, but rather obtains its reference. This ensures the loose coupling of the client and server-side objects, while also optimizing the performance of the communication.

1. Two kinds of channels of remoting

There are two main types of remoting channels: TCP and HTTP. In. NET, the IChannel interface is defined in System.Runtime.Remoting.Channel. The IChannel interface includes the TcpChannel channel type and the HTTP channel type. They correspond to these two types of remoting channels respectively.

The TcpChannel type is placed in the namespace SYSTEM.RUNTIME.REMOTING.CHANNEL.TCP. The TCP channel provides a socket based transport tool that uses the TCP protocol to transmit serialized message flows across the remoting boundary. The TcpChannel type uses the binary format to serialize the message object by default, so it has higher transport performance. The HttpChannel type is placed in the namespace System.Runtime.Remoting.Channel.Http. It provides a way to use the HTTP protocol to transmit a serialized message stream across the Internet over the firewall. By default, the HttpChannel type uses SOAP format to serialize message objects, so it has better interoperability. Usually in the LAN, we use TcpChannel more; If you want to cross the firewall, use HttpChannel.

2. How to activate a remote object

Before accessing an object instance of a remote type, it must be created and initialized through a process named activation. This client creates a remote object through a channel, called the activation of the object. In remoting, the activation of a remote object is divided into two broad categories: server-side activation and client activation.

  (1) server-side activation , also known as Wellknow Way, many are translated into well-known objects. Why is it called a well-known object activation mode? is because the server application publishes this type on a well-known Uniform Resource Identifier (URI) before activating the object instance. The server process then configures a WellKnown object for this type and publishes the object based on the specified port or address.. Net Remoting the server-side activation into singleton mode and SingleCall mode.

Singleton mode: This is a stateful mode. If set to singleton activation mode, remoting will establish the same object instance for all clients. When an object is active, the singleton instance processes all subsequent client access requests, whether they are the same client or another client. The singleton instance will always maintain its state in the method invocation. For example, if a remote object has an additive method (I=0;++i), it is invoked by multiple clients (for example, two). If set to Singleton, the first customer gets a value of 1 and the second customer gets a value of 2 because they get the same object instance. If you are familiar with state management, we can think of it as a application state.

SingleCall mode: SingleCall is a stateless mode. Once set to SingleCall mode, when the client invokes the method of the remote object, remoting establishes a remote object instance for each client, and the destruction of the object instance is automatically managed by the GC. As in the previous example, two customers accessing a remote object get 1. We can still learn from's state management and consider it a session state.

  (2) client activation . Unlike the wellknown mode, remoting assigns a URI to each client-activated type when activating each instance of the object. Client activation mode Once the client's request is received, an instance reference is established for each client. There is a difference between the SingleCall mode and the client activation mode: First, the object instance is created in a different time. Client activation is instantiated when a request is made by the customer, and SingleCall is created when the object method is invoked. Second, the object being activated by the SingleCall mode is stateless, and the management of the object lifetime is managed by the GC, while the client-activated object has a state and its lifecycle is customizable. Third, the two activation modes are implemented differently on the server side and the client. Especially on the client side, the SingleCall mode is activated by GetObject (), which invokes the object's default constructor. While the client-activated mode is activated by CreateInstance (), it can pass parameters, so a custom constructor can be invoked to create the instance.

Second, the definition of remote objects

As mentioned earlier, when a client obtains a server-side object, it does not get the actual service-side object, but rather gets its reference. Therefore, in remoting, there are some necessary definition specifications for the remote object to follow.

Because the objects passed by remoting are referenced, the remote object class that is passed must inherit MarshalByRefObject. MSDN's description of MarshalByRefObject is that MarshalByRefObject is the base class for objects that communicate across application domain boundaries by exchanging messages using proxies. Objects that are not inherited from MarshalByRefObject are marshaled implicitly by value. When a remote application references an object that is marshaled by value, a copy of the object is passed across the remoting boundary. Because you want to communicate using proxy methods rather than replica methods, you need to inherit marshallbyrefobject.

The following is the definition of a remote object class:

public class Serverobject:marshalbyrefobject
{public person
 getpersoninfo (string name,string sex,int age)
 {person person
 = new person ();
 Person. name = name;
 Person. Sex = Sex;
 Person. Age = age;
 return person;

This class only implements the simplest method of setting up a person's basic information and returning a man class object. Note the person class returned here. Since the person passed here is done in the form of a value, and the remoting requirement must be a referenced object, the person class must be serialized.

Therefore, in a remote object in remoting, if you also want to invoke or pass an object, such as a class, or structure, the class or structure must be serialized

[Serializable] public class Person {public person

 private string name;
 private string sex;
 private int age;

 public string name
 set {name = value;}

 public string Sex
 {return Sex;}
 set {sex = value;}

 public int Age
 {get {return age
 set {age = value;}

The remote object is compiled into a DLL as a class library. This DLL will be placed on both the server side and the client to add references.

The remote objects that can be passed in remoting may be of various types, including complex dataset objects, as long as it can be serialized. Remote objects can also contain events, but server-side handling of events is special, which I'll cover in the third of this series.

Third, server-side

According to the first section, different server-side implementations of the channel type vary according to the activation mode. Generally speaking, the server side should be divided into three steps:

1. Registered Channel

To communicate across an application domain, you must implement a channel. As mentioned earlier, remoting provides a ichannel interface that contains the TcpChannel and HttpChannel two types of channels. These two types, in addition to the format of the performance and serialization data, are implemented in exactly the same way, so let's take TcpChannel for example.

To register TcpChannel, first add the reference "System.Runtime.Remoting" to the project, and then the using namespace: SYSTEM.RUNTIME.REMOTING.CHANNEL.TCP. The code is as follows:

TcpChannel channel = new TcpChannel (8080);
ChannelServices.RegisterChannel (channel);

When the channel object is instantiated, the port number is passed as a parameter. The static method RegisterChannel () is then invoked to register the channel object.

2, register the remote object

After registering the channel, to be able to activate the remote object, you must register the object in the channel. Depending on the activation mode, the method of registering the object is different.

(1) Singleton mode

For WellKnown objects, the static method RemotingConfiguration.RegisterWellKnownServiceType () can be implemented:

RemotingConfiguration.RegisterWellKnownServiceType (
 typeof (Serverremoteobject.serverobject),
 " Servicemessage ", Wellknownobjectmode.singleton);

(2) SingleCall mode

The method of registering the object is basically the same as the singleton mode, just change the enumeration parameter WellKnownObjectMode to SingleCall.

RemotingConfiguration.RegisterWellKnownServiceType (
 typeof (Serverremoteobject.serverobject),
 " Servicemessage ", WellKnownObjectMode.SingleCall);

(3) Client activation mode

For the client activation mode, the use of the method is different, but the difference is not small, look at the code at a glance.

Remotingconfiguration.applicationname = "Servicemessage";
Remotingconfiguration.registeractivatedservicetype (
 typeof (Serverremoteobject.serverobject));

Why do you set the ApplicationName property before registering the object method? In fact, this property is the URI of the object. For the wellknown pattern, the URI is placed in the parameters of the RegisterWellKnownServiceType () method, and of course it can be taken out specifically to assign values to the ApplicationName property. In the overload of the Registeractivatedservicetype () method, there is no applicationname parameter, so it must be separated.

3. Logout Channel

If you want to turn off the remoting service, you need to unregister the channel, or you can turn off monitoring of the channel. When we register the channel in the remoting, it automatically opens the channel listening. If listening to the channel is turned off, the channel cannot accept the client's request, but the channel still exists and throws an exception if you want to register the channel again.

Obtain the currently registered channel;
ichannel[] channels = channelservices.registeredchannels;

Closes the specified channel named mytcp;
foreach (IChannel eachchannel in Channels)
 if (eachchannel.channelname = "mytcp")
 TcpChannel TcpChannel = (TcpChannel) eachchannel;

 Turn off listening;
 tcpchannel.stoplistening (null);

 Logout channel;
 Channelservices.unregisterchannel (TcpChannel);

Code, the Registerdchannel property obtains the currently registered channel. In remoting, it is allowed to register multiple channels at the same time, which is explained later.

Four, the client

The client mainly does two things, one is registers the channel. As you can see from figure one, both the server side and the client in remoting must pass the message through the channel to get the remote object. The second step is to get the remote object.

1. Registered Channel:

TcpChannel channel = new TcpChannel ();
ChannelServices.RegisterChannel (channel);

Note When the client instantiates the channel, it is the default constructor that is invoked, that is, no port number is passed. In fact, this port number is integral, except that its designation is followed as part of the URI.

2, get the remote object.

As with the server side, different activation modes determine how the client is implemented differently. The difference, however, is only the difference between the wellknown activation mode and the client activation mode, and the client implementation is exactly the same for the Singleton and SingleCall modes.

(1) WellKnown activation mode

To obtain a well-known remote object on the server side, you can obtain it through the GetObject () method of the Activator process:

Serverremoteobject.serverobject ServerObj = (serverremoteobject.serverobject) activator.getobject (
 typeof ( Serverremoteobject.serverobject), "Tcp://localhost:8080/servicemessage");

First activated in wellknown mode, the client obtains the object by using GetObject (). Where the argument first is the type of the remote object. The second parameter is the server-side URI. If it is an HTTP channel, it is natural to use Http://localhost:8080/ServiceMessage. Because I am using the local machine, so here is the localhost, you can use the specific server IP address to replace it. The port must be consistent with the server-side port. The following is the server-defined remote object service name, which is the contents of the ApplicationName property.

(2) Client activation mode

As mentioned earlier, the wellknown mode can only invoke the default constructor when the object is created by the client, as the above code illustrates, because the GetObject () method cannot pass the parameters of the constructor. The client-activated mode can create a remote object through a custom constructor.

There are two methods of client activation mode:
1) Call the RemotingConfiguration static method Registeractivatedclienttype (). This method returns a value of void, which simply registers the remote object with the client. A specific instantiation also requires invoking the constructor of the object class.

Remotingconfiguration.registeractivatedclienttype (    
 typeof (Serverremoteobject.serverobject),
 "tcp:// Localhost:8080/servicemessage ");
Serverremoteobject.serverobject serverobj = new Serverremoteobject.serverobject ();

2) The CreateInstance () method that invokes the process activator. This method creates a class object of the type specified by the method parameter. Unlike the previous GetObject (), it calls the constructor on the client, while GetObject () simply obtains the object, and the creation instance is done on the server side. The CreateInstance () method has many overloads, and I'll focus on the two commonly used.

A, public static object CreateInstance (type type, object[] args, object[] activationattributes);

Parameter description:
Type: The kind of object to create.
Args: An array of arguments that match the number, order, and type of arguments to invoke the constructor. If args is an empty array or a null reference (Nothing in Visual Basic), a constructor (default constructor) with no arguments is invoked.
Activationattributes: Contains one or more arrays of properties that can participate in activation.

The parameter args here is a object[] array type. It can pass parameters in the constructor of the object to be created. From here you can actually get a conclusion: The remote object class passed by the WellKnown activation mode can only use the default constructor, while the activated mode allows the user to customize the constructor. The activationattributes parameter is typically used in this method to pass the URL of the server.
Suppose our remote object class Serverobject has a constructor:

Serverobject (String pname,string psex,int pAge)
 name = PName;
 sex = psex;
 age = PAge;

The code to implement is:

Object[] Attrs = {new Urlattribute ("Tcp://localhost:8080/servicemessage")};
object[] Objs = new Object[3];
Objs[0] = "Wayfarer";
Objs[1] = "male";
OBJS[2] =;
Serverremoteobject.serverobject = Activator.CreateInstance (
 typeof (Serverremoteobject.serverobject), OBJS, ATTRS);

As you can see, the objs[] array passes the parameters of the constructor.

b, public static ObjectHandle CreateInstance (String assemblyname, String TypeName, object[] activationattribute);

Parameter description:
AssemblyName: The name of the assembly in which to look for the type named TypeName. If AssemblyName is a null reference (Nothing in Visual Basic), the executing assembly is searched.
TypeName: The name of the preferred type.
Activationattributes: Contains one or more arrays of properties that can participate in activation.

Parameter description at a glance. Note that this method returns a value of ObjectHandle type, so the code is different from the previous one:

Object[] Attrs = {new Urlattribute ("Tcp://localhost:8080/echomessage")};   
ObjectHandle handle = Activator.CreateInstance ("Serverremoteobject",
 "Serverremoteobject.serverobject", Attrs) ;
Serverremoteobject.serverobject obj = (serverremoteobject.serverobject) handle. Unwrap ();

This method is actually the default constructor for the call. The Objecthandle.unwrap () method returns the Wrapped object.

Note: To use Urlattribute, you also need to add in the namespace: using System.Runtime.Remoting.Activation;

V. Supplement to the Remoting Foundation

Through the above description, basically has completed a simplest remoting program. This is a standard method of creating a remoting program, but in the actual development process, we encountered the situation may be strange, if only master a so-called "standard", on the delusion can be "a recruit fresh, eat the day", is impossible.

1. Register multiple channels

In remoting, you allow multiple channels to be created at the same time, that is, different channels are created based on different ports. However, remoting requires the name of the channel to be different because it is used as a unique identifier for the channel. Although IChannel has channelname properties, this property is read-only. Therefore, the method of creating the channel described above cannot achieve the requirement of registering multiple channels simultaneously.

At this point, we have to use the IDictionary interface in System.Collection:

To register a TCP channel:

IDictionary Tcpprop = new Hashtable ();
tcpprop["name"] = "tcp9090";
tcpprop["port"] = 9090;
IChannel channel = new TcpChannel (Tcpprop,
 new Binaryclientformattersinkprovider (),
 new BinaryServerFormatterSinkProvider ());
ChannelServices.RegisterChannel (channel);

Register HTTP Channel:

IDictionary Httpprop = new Hashtable ();
httpprop["name"] = "http8080";
httpprop["port"] = 8080;
IChannel channel = new HttpChannel (Httpprop,
 new Soapclientformattersinkprovider (),
 new Soapserverformattersinkprovider ());
ChannelServices.RegisterChannel (channel);

In the name attribute, it is OK to define a different channel name.

2. Remote object Metadata Dependencies

Because remote objects are used by both the server side and the client, the usual way is to generate two identical object DLLs, adding references separately. However, for the security of the Code and to reduce the client's dependency on remote object metadata, it is necessary to make changes in this way. That is, the remote object is implemented on the server side, and the metadata for those implementations is deleted at the client.

Because the activation mode is different, the method of creating objects on the client is different, so the dependencies of the metadata should be separated into two cases.

(1) WellKnown activation mode:

Implemented through an interface. On the server side, provides interfaces and implementation of specific classes, while the client only provides interfaces:

Public interface Iserverobject
 getpersoninfo (string name,string sex,int age);

public class Serverobject:marshalbyrefobject,iserverobject

Note: The name of the object assembly must be the same on both sides, strictly speaking, the name of the namespace must be the same.

(2) Client activation mode:

As mentioned earlier, for client activation mode, either by using a static method or by using the CreateInstance () method, you must instantiate an object on the client call constructor. So, on the client we provide the remote object, we can not only provide interfaces, but not the implementation of the class. In fact, to separate from the remote object metadata, there are two ways to choose:

A, use the wellknown activation mode to simulate the client activation mode:

By taking advantage of the "abstract factory" in design mode, the following class diagram describes the overall solution:

We add the interface and implementation classes of the abstract factory to the remote object on the server side:

Public interface Iserverobject
 getpersoninfo (string name,string sex,int age);

Public interface iserverobjfactory
 iserverobject CreateInstance ();  

public class Serverobject:marshalbyrefobject,iserverobject
{public person
 getpersoninfo (string name, String Sex,int Age
 } {person person
  = new person ();
  Person. name = name;
  Person. Sex = Sex;
  Person. Age = age;
  return person;

public class Serverobjfactory:marshalbyrefobject,iserverobjfactory
 iserverobject CreateInstance ()
  new Serverobject ();

Then only the factory interface and the original object interface are provided in the remote object of the client:

Public interface Iserverobject
 getpersoninfo (string name,string sex,int age);

Public interface iserverobjfactory
 iserverobject CreateInstance ();  

We use the WellKnown activation mode to register the remote object on the server side:

passing objects;
RemotingConfiguration.RegisterWellKnownServiceType (
 typeof ( serverremoteobject.serverobjfactory),
 "Servicemessage", WellKnownObjectMode.SingleCall);

Note that this registration is not a Serverobject class object, but a Serverobjfactory class object.


Serverremoteobject.iserverobjfactory serverfactory =    
 (serverremoteobject.iserverobjfactory) Activator.GetObject (
 typeof (Serverremoteobject.iserverobjfactory),
 "tcp://localhost:8080/ Servicemessage ");

Serverremoteobject.iserverobject ServerObj = Serverfactory.createinstance ();

Why is this an impersonation of a client-activated model? From the activation method, we use the SingleCall mode to activate the object, but at this time we are not the remote object we want to pass, but the factory object. If the client wants to create a remote object, it should also be obtained through the CreateInstance () method of the Factory object. And this method is called on the client. Therefore, it is implemented in the same way as the client activation mode.

B. Replace the metadata of remote objects with alternative classes

In fact, we can use a trick to deceive remoting. The alternative class here is the trick. Now that the service is provided, the details of the implementation of the remote objects Remoting pass are, of course, on the server side. And to put the copy of the object on the client, but because the client must call the constructor, and take the helpless. Since the specific implementation is on the server side, and in order to be able to instantiate at the client, then this is done on the client. As for the details of implementation, there is no need to worry.

If the remote object has a method, the server side provides the method implementation, and the client provides this method OK, as for the inside implementation, you can either throw an exception, or return a null value; If the method returns void, then the inside can be empty. The key is to have this method for this client class object. The implementation of this method, in fact, and the declaration of the method is similar, so I said it is a trick. The same is true for constructors.

Or use code to illustrate this "conspiracy", more intuitive:

Server side:

public class Serverobject:marshalbyrefobject
 serverobject ()

 Getpersoninfo (String name,string sex,int age)
 {person person
  = new person ();
  Person. name = name;
  Person. Sex = Sex;
  Person. Age = age;
  return person;


public class Serverobject:marshalbyrefobject
 serverobj ()
  throw new System.NotImplementedException ();

 Public person Getpersoninfo (string name,string sex,int age)
  throw new system.notimplementedexception ();

Compare client and server side, client Method Getpersoninfo (), no specific implementation details, just throw an exception. Or simply write the statement return null, OK. We call this class of the client a substitute class for the remote object.

3, using the configuration file to achieve

The methods described earlier, the settings for the server URI, port, and activation mode are done in code. In fact, we can also use the configuration file to set. This is a good thing, because this profile is an XML document. If you need to change the port or other, we do not need to modify the program, and recompile, but only need to change the configuration file.

(1) Server-side configuration file:

 <application name= "serverremoting" >
  <wellknown mode= "Singleton" type= "Serverremoteobject.serverobject" Servicemessage "/>
   <channel ref=" tcp "port=" 8080 "/>

If it is a client-activated mode, change the wellknown to activated and delete the mode attribute.

Place the configuration file in the application folder of the server program, named Serverremoting.config. Then the previous server-side program can use this statement directly:

RemotingConfiguration.Configure ("Serverremoting.config");

(2) Client configuration file

If it is a client-activated mode, the modification is the same as above. The call also uses the RemotingConfiguration.Configure () method to invoke the configuration file stored on the client.

The configuration file can also be placed in Machine.config. If the client program is a Web application, it can be placed in web.config.

4. Start/Close Specify remote object

Remoting does not provide a method similar to the Unregisterwellknownservicetype (), which means that once the remote object is registered, the object remains in the channel if the channel is not closed. As long as the client activates the object, an object instance is created. If the remoting transmits only a remote object, there is no problem, and closing the channel is OK. What if you are transferring multiple remote objects? What should I do to close the specified remote object? What if you need to start after shutting down?

We note that the marshal () and disconnect () methods are provided in the remoting, and the answer is here. The Marshal () method converts the MarshalByRefObject class object to the ObjRef class object, which is all the relevant information needed to store the build agent to communicate with the remote object. This allows the instance to be serialized so that it can be transferred between the application domains and over the network, and the client will be able to invoke it. The disconnect () method interrupts the specific instance object from the channel.

The method is as follows:
Register the Channel first:

TcpChannel channel = new TcpChannel (8080);
ChannelServices.RegisterChannel (channel);

Then start the service:
The remote object is instantiated first on the server side.

Serverobject obj = new Serverobject ();

Then, register the object. Note that there is no remotingconfiguration.registerwellknownservicetype (), but use Remotingservices.marshal ():

ObjRef Objrefwellknown = Remotingservices.marshal (obj, "servicemessage");

If you want to unregister an object, then:

RemotingServices.Disconnect (obj);

  Note that the class object disconnect here must be the previously instantiated object. Because of this, we can create the specified remote object as needed, and when closed, disconnect the object that was instantiated before.

As for the client invocation, the same method as the previous wellknown mode is still available through Activator.GetObject (). However, from the implementation code, we notice a problem, because the server side is an explicit instantiation of the remote object, so regardless of the number of clients, whether the same, they call the same remote object. So we call this method the simulated singleton pattern.

Client activation mode

We can also simulate the client activation mode by marshal () and disconnect (). First, we review the "remote Object Metadata Dependencies" section, where I talked about using the "abstract factory" of design patterns to create object instances to simulate the client activation mode using the SingleCall pattern. Think carefully about the previous simulation of the singleton mode. Is that the answer?

In "simulated singleton" mode, we are marshal a specific instance of a remote object to allow the client to obtain reference information for that object. So let's change the idea that when we use an abstract factory to provide the interface, the factory class implements the method of creating the remote object. Then we create the factory class instance on the server side. Then the factory class instance is marshal. When the client gets the object, instead of getting the specific remote object, it gets the specific factory class object. Then call the CreateInstance () method to create a concrete instance of the remote object. At this point, the same factory class object is invoked for multiple clients, but the remote object is created by the client itself, so for the remote object, it is activated by the client, creating a different object.

When we want to start/close the specified object, we only need to use the Disconnet () method to unregister the factory class object.

Vi. Summary

Microsoft.NET Remoting really can be said to be profound. The content of the whole remoting is not what I can do in this little article, not even the beginner of my remoting can master. Wang Guowei wrote in the book: "Ancient and modern" into a major cause of great scholars, must go through three kinds of realm. Last night, the West wind trees, alone on the high-rise, hope to do the Tianya road. "This first realm also. "Shiriko end not regret, for Iraq to eliminate people haggard." "This second realm is also. "The public in search of his 1100 degrees, suddenly looking back, the person is in the lights dim place." "This third realm also. such as to describe my study of remoting, still in the "alone on the high-rise, look at the end of the world road", it can be said that has not yet been in.

Perhaps need to "Shiriko", learn Remoting "never regret", just can "suddenly look back" bar.

The above is a. Net remoting the whole content of the foundation, hope to give everybody a reference, also hope everybody support cloud habitat community a lot.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.