In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces the example analysis of Remoting event handling in Microsoft .net Remoting. The article is very detailed and has certain reference value. Interested friends must read it!
Handling events in Remoting is not complicated, but there are some skills you need to dig out. It is these techniques, like strict barriers, that frighten many people, or do not know what they call them, and finally give up the use of events in Remoting. There is also a lot of discussion on this topic on the Internet, and there are a lot of related technical articles. Unfortunately, many of the articles are not comprehensive. When I was studying Remoting, I was also interested in event handling. After referring to the relevant books and documents, and after repeated experiments, I am convinced that I can explain this problem clearly.
Download the sample code for this article:
Remoting event (client sends fax)
Remoting event (server broadcast)
Remoting event (server broadcast improvement)
The distributed processing program that applies Remoting technology usually includes three parts: remote object, server and client. Therefore, from the direction of the event, there should be three forms:
1. The server subscribes to client events
2. The client subscribes to server events
3. The client subscribes to client events
The server subscribes to the client event, that is, the client sends the message, the server captures the message, and then responds to the event, which is equivalent to sending a fax from the subordinate to the superior. Conversely, when the client subscribes to the server event, the server sends the message. At this time, all clients capture the message and fire the event, which is equivalent to a system broadcast. What about client subscriptions to client events? It's like talking. A message is sent by one client, and other clients capture the message and fire the event. Unfortunately, I didn't find a solution to talk in private. When the client sends a message, anyone who subscribes to the event will get the information.
However, no matter which way it is, it is the remote object that really contains the event. The principle is very simple, let's think about it, what is the content passed between the client and the server in Remoting? No doubt, it's a remote object. Therefore, the event message we pass is naturally wrapped by a remote object. This is like EMS express, the remote object is the car carrying the letter, and the event message is the letter loaded by the car. As for the direction of event delivery, it's just that the roles of senders and subscribers have changed.
First, the server subscribes to client events
It is relatively simple for the server to subscribe to client events. Let's take sending a fax as an example. First of all, we must have a fax machine and the documents to be faxed, which is like our remote object. And the fax machine must have a "send" operation button. This is like a delegate in a remote object. When a customer sends a fax, a method of sending a message needs to be activated on the client, which is like pressing the send button. After the message is sent to the server, it triggers an event that the server subscribes to. After the server gets the event message, it will deal with the related business. This is like the person who receives the fax, when the fax is received, he will hear the sound of being connected, and when he chooses "receive", the message will be captured.
Now, let's simulate this process. First define the remote object, which should handle the business of sending faxes:
The first is the public interface (Common.dll) of the remote object:
Public delegate void FaxEventHandler (string fax); public interface IFaxBusiness {void SendFax (string fax);}
Notice that in the public interface assembly, a public delegate is defined.
Then we define a remote object class (FaxBusiness.dll) that specifically handles the fax business, in which we first add a reference to the common interface assembly:
Public class FaxBusiness:MarshalByRefObject,IFaxBusiness {public static event FaxEventHandler FaxSendedEvent; # region public void SendFax (string fax) {if (FaxSendedEvent! = null) {FaxSendedEvent (fax);}} # endregion public override object InitializeLifetimeService () {return null;}}
In this remote object, the event type is the delegate type that we defined in the public assembly Common.dll. SendFax implements the methods in the interface IFaxBusiness. The signature of this method is the same as the defined delegate, which calls the event FaxSendedEvent.
The special thing is that the remote object we define is best to override the InitializeLifetimeService () method of the MarshalByRefObject class. Returning a null value indicates that the life cycle of the remote object is infinite. Why rewrite this method? The truth is self-evident that if the lifecycle is not limited, the event cannot be activated once the lifecycle of the remote object ends.
The next step is to implement the client and the server, respectively. The server is a Windows application with the following interface:
When we load the form, we register the channel and remote objects:
Private void ServerForm_Load (object sender, System.EventArgs e) {HttpChannel channel = new HttpChannel (8080); ChannelServices.RegisterChannel (channel); RemotingConfiguration.RegisterWellKnownServiceType (typeof (FaxBusiness), "FaxBusiness.soap", WellKnownObjectMode.Singleton); FaxBusiness.FaxSendedEvent + = new FaxEventHandler (OnFaxSended);}
We are in SingleTon mode and register a remote object. Notice how this code differs from a normal Remoting server? By the way, it has an extra line of code to register the event:
FaxBusiness.FaxSendedEvent + = new FaxEventHandler (OnFaxSended)
This line of code, like our server-side fax machine, has been switching to "automatic" mode. It always listens for fax messages from the client, and once the fax message is sent from the client, it responds to the event method, that is, the OnFaxSended method:
Public void OnFaxSended (string fax) {txtFax.Text + = fax; txtFax.Text + = System.Environment.NewLine;}
This method is very simple, which is to display the Fax sent by the client to the txtFax text box control.
What about the client? It is still a Windows application. The code is very simple, first of all, for simplicity, we still have it activate the remote object when the form is loaded:
Private void ClientForm_Load (object sender, System.EventArgs e) {HttpChannel channel = new HttpChannel (0); ChannelServices.RegisterChannel (channel); faxBus = (IFaxBusiness) Activator.GetObject (typeof (IFaxBusiness), "http://localhost:8080/FaxBusiness.soap");}
Well, it can be said that the way the client activates an object is no different from a normal Remoting client application. It's time to write a fax! We put a text box object on the form and change its Multiline property to true. Put another button to send the fax:
Private void btnSend_Click (object sender, System.EventArgs e) {if (txtFax.Text! = String.Empty) {string fax = "fax from" + GetIpAddress () + "client:" + System.Environment.NewLine; fax + = txtFax.Text; faxBus.SendFax (fax);} else {MessageBox.Show ("Please enter fax content!");}} private string GetIpAddress () {IPHostEntry ipHE = Dns.GetHostByName (Dns.GetHostName ()); return ipHE.AddressList [0] .ToString ();}
In this button click event, you just need to call the SendFax () method of the remote object faxBus to OK, which is very simple. But wait, why does your code have so many lines? In fact, it's not surprising. I just thought that there might be a lot of customers sending faxes. In order to prevent the server staff from getting confused and not knowing who sent it, they are required to add their respective signatures on the fax, that is, the IP address of the client. Now that you want to get the IP address of your computer, be sure to add a namespace reference to DNS:
Using System.Net
Because we strictly follow the way distributed processors are deployed, we only need to add a reference to the common assembly (Common.dll) on the client side. On the server side, you must add references to both the public assembly and the remote object assembly.
OK, program completed, let's take a look at this rudimentary fax machine:
Client:
Hey hey, I dream of having a holiday. OK, the fax is ready, send it! Take a look at the server, great, the boss has received my request for leave fax!
Second, client subscribes to server events
Hey, eat sugar cane first to eat sweet section, do things I also like to do easy first. Now that the good days are over, it's time to suffer. Let's recall the implementation method just now, and then think about how to implement client-side subscription server events.
In the previous section, the event is placed in a remote object, and after the client activates the object, you can send a message. On the server side, you only need to subscribe to the event. Now the idea should be reversed, with the client subscribing to the event and the server sending the message. Is it so simple? Don't be happy too soon. Let's think about it. Who did the task of sending messages? Is a remote object. When was the remote object created? We think carefully about several ways to activate Remoting, whether it is server-side activation or client-side activation, their working principle is that the client determines when the server creates a remote object instance, such as calling the method of the remote object. The server's job is to register the remote object.
Recall the server code for these three activation methods:
SingleCall activation method:
RemotingConfiguration.RegisterWellKnownServiceType (typeof (BroadCastObj), "BroadCastMessage.soap", WellKnownObjectMode.Singlecall)
SingleTon activation method:
RemotingConfiguration.RegisterWellKnownServiceType (typeof (BroadCastObj), "BroadCastMessage.soap", WellKnownObjectMode.Singleton)
Client activation method:
RemotingConfiguration.ApplicationName = "BroadCastMessage.soap" RemotingConfiguration.RegisterActivatedServiceType (typeof (BroadCastObj))
Please pay attention to the word Register, which means registration. That is, the creation of a remote object instance is not shown on the server side. Without this instance, how can you broadcast the message?
Some people may wonder, after registering a remote object, wouldn't it be fine to explicitly instance the object? That is, add this code after registration:
BroadCastObj obj = new BroadCastObj ()
However, we need to understand the fact that the server and the client are in two different application domains. Therefore, in Remoting, the remote object obtained by the client is actually the proxy of the registered object on the server side. If we manually create an instance after registration instead of the object that Remoting automatically creates after activation, then the object obtained by the client and the manually created instance on the server are two very different objects. The proxy object obtained by the client does not point to the obj instance you just created. So messages sent by obj cannot be captured by the client at all.
So, we can only look at the ocean and sigh and there is nothing we can do about it? Don't worry, don't forget that in the server registration object method, there is another method, the Marshal method. Do you remember how to implement Marshal?
BroadCastObj Obj = new BroadCastObj (); ObjRef objRef = RemotingServices.Marshal (Obj, "BroadCastMessage.soap")
This method is not the same as before. In the first three ways, the remote object is created automatically according to the way the client invokes it. What about the Marshal method? The remote object instance is explicitly created and then Marshal into the channel to form a proxy for the ObjRef pointing to the object. This object exists as long as the life cycle does not end. At this point, the object obtained by the client is the proxy of the created Obj instance.
OK, this problem is solved. Let's take a look at the implementation.
Public assemblies and remote objects are similar, so I will not repeat them, but only attach the code:
Public assembly:
Public delegate void BroadCastEventHandler (string info); public interface IBroadCast {event BroadCastEventHandler BroadCastEvent; void BroadCastingInfo (string info);}
Remote object class:
Public event BroadCastEventHandler BroadCastEvent;#region IBroadCast member / / [OneWay] public void BroadCastingInfo (string info) {if (BroadCastEvent! = null) {BroadCastEvent (info);}} # endregionpublic override object InitializeLifetimeService () {return null;}
Next, it's time to implement the server side. Before I realize it, I'd like to say a few more words. In the first section, we implemented the server-side subscription client event. Because the subscription event occurs on the server side, the event itself is not delivered. What is serialized is only the delivered message, that is, Fax. Now, the direction has changed, the server delivers the message, and the client subscribes to the event. However, this event is placed in the remote object, so the event must be serialized. In .net Framework1.1, Microsoft limits the security level of serialization. Serialization and deserialization of delegates and events is disabled by default, so we should set the property value of TypeFilterLevel to the Full enumeration value. So the way you register a channel on the server side changes:
Private void StartServer () {BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider (); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider (); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable (); props ["port"] = 8080; HttpChannel channel = new HttpChannel (props,clientProvider,serverProvider); ChannelServices.RegisterChannel (channel); Obj = new BroadCastObj (); ObjRef objRef = RemotingServices.Marshal (Obj, "BroadCastMessage.soap");}
Note that the statement serverProvider.TypeFilterLevel = TypeFilterLevel.Full; sets the serialization security level. To use the TypeFilterLevel attribute, you must declare the namespace:
Using System.Runtime.Serialization.Formatters
The next two statements are to register the remote object. Because in my broadcast program, sending a broadcast message is placed in another window, I declare the remote object as a public static object:
Public static BroadCastObj Obj = null
Then add the following to the call window event:
Private void ServerForm_Load (object sender, System.EventArgs e) {StartServer (); lbMonitor.Items.Add ("Server started!");}
To take a look at the interface, first start the server main window:
I put a ListBox control to display some information, such as showing that the server has started. The BroadCast button broadcasts messages. Clicking this button will bring up a dialog box:
Code for the BraodCast button:
Private void btnBC_Click (object sender, System.EventArgs e) {BroadCastForm bcForm = new BroadCastForm (); bcForm.StartPosition = FormStartPosition.CenterParent; bcForm.ShowDialog ();}
In the dialog box, the main thing is the Send button:
If (txtInfo.Text! = string.Empty) {ServerForm.Obj.BroadCastingInfo (txtInfo.Text);} else {MessageBox.Show ("Please enter information!") ;}
But it's simple to call the sending message method of the remote object.
Now it's time to implement the client. We can refer to the previous example and just change the server to the client. In addition, considering the serialization security level, the code would look like this:
Private void ClientForm_Load (object sender, System.EventArgs e) {BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider (); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider (); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable (); props ["port"] = 0; HttpChannel channel = new HttpChannel (props,clientProvider,serverProvider); ChannelServices.RegisterChannel (channel); watch = (IBroadCast) Activator.GetObject (typeof (IBroadCast), "http://localhost:8080/BroadCastMessage.soap"); watch.BroadCastEvent + = new BroadCastEventHandler (BroadCastingMessage);}
Note that the port number of the client channel should be set to 0, which means that the client automatically selects the available port number. If you want to set to the specified port number, you must ensure that it is different from the port number of the server channel.
Then, the method of the BroadCastEventHandler delegate:
Public void BroadCastingMessage (string message) {txtMessage.Text + = "I got it:" + message; txtMessage.Text + = System.Environment.NewLine;}
The client interface is shown in the figure:
OK, let's run this program with great expectation. Start the server application first, and then start the client. Oh, no, there is an error message!
"when it comes to unsatisfactory things, ten often occupy eighty-nine." Don't be depressed, let's analyze the reason. First look at the error message, which reports that we did not find the Client assembly. In fact, however, Client assemblies do exist. So let's debug it again, which step is the problem? Set the breakpoint and track it sentence by sentence. The previous registration channel is fine, and when running to the watch.BroadCastEvent + = new BroadCastEventHandler (BroadCastingMessage) statement, an error occurs!
That is, the creation of the remote object was successful, but failed when subscribing to the event. What is the reason? It turns out that the client's delegate is obtained through serialization, and when subscribing to the event, the delegate attempts to load the assembly that contains the same method as the signature, that is, the assembly Client where the BroadCastingMessage method is located. However, this loading process occurs on the server side, and on the server side, no Client assembly exists, so the above exception occurs naturally.
The reason is clear, how to solve it? First of all, the BroadCastingMessage method must be on the client side, so inevitably, the process of delegating to load the Client assembly must also be done on the client side. The server event is captured by the remote object, so the remote object event must be registered on the client side. One requirement must be on the client side and the other must be on the server side, and things contradict each other.
So, let's think about such an example first. Suppose we want to exchange the values of x and y, which should be done like this? It's simple to introduce an intermediate variable.
Int Xanthi 1 ~ 2 ~ z ~ z = x ~ * * x = y ~ * *
I believe everyone will play this game, so good, we also need to introduce such a "middle" object. This intermediate object is exactly the same as the original remote object in terms of event handling:
Public class EventWrapper:MarshalByRefObject {public event BroadCastEventHandler LocalBroadCastEvent; / / [OneWay] public void BroadCasting (string message) {LocalBroadCastEvent (message);} public override object InitializeLifetimeService () {return null;}}
The difference, however, is that this Wrapper class must be deployed on both the client and the server, so it should be placed in the public assembly Common.dll.
Now let's modify the original client code:
Watch = (IBroadCast) Activator.GetObject (typeof (IBroadCast), "http://localhost:8080/BroadCastMessage.soap"); watch.BroadCastEvent + = new BroadCastEventHandler (BroadCastingMessage))
Modified to:
Watch = (IBroadCast) Activator.GetObject (typeof (IBroadCast), "http://localhost:8080/BroadCastMessage.soap");EventWrapper wrapper = new EventWrapper (); wrapper.LocalBroadCastEvent + = new BroadCastEventHandler (BroadCastingMessage); watch.BroadCastEvent + = new BroadCastEventHandler (wrapper.BroadCasting))
Why is it OK to do so? Maybe it's easy to draw a picture, but my artistic talent is really bad, and I hope I can improve it in the future. Let's explain it in words.
As mentioned earlier, the delegate loads the client assembly. Now let's hand over the right to delegate the load of the remote object to EventWrapper. Because this class object is placed on the client side, it has no problem loading the client assembly. Statement:
EventWrapper wrapper = new EventWrapper (); wrapper.LocalBroadCastEvent + = new BroadCastEventHandler (BroadCastingMessage)
This function is realized.
However, although the event is subscribed to at this time, the event is still on the client side and is not associated with the server. Events on the server side are placed in the remote object, so subscribe to the event, which is done by the remote object watch. But at this point it no longer subscribes to BroadCastingMessage, but to BroadCasting, the trigger event method of EventWrapper. Then the delegate will also load the assembly at this time, but at this time it will be the assembly where the BroadCasting is located. Because the loading takes place on the server side. Well, happily, the assembly in which BroadCasting is located is the public assembly (as mentioned earlier, EventWrapper should be placed in the public assembly Common.dll), and the public assembly is deployed on both the server side and the client side. Naturally, there will be no problem of not finding the assembly.
Note: EventWrapper still inherits the MarshalByRefObject class because it overrides the InitializeLifetimeService () method.
Now let's run the program again. First run the server; then run the client, OK, and the client form appears:
Then we click the "BroadCast" button on the server to send a broadcast message:
Click "Send" to send, and then look at the client, what will happen? Fine,I got it!
What do you think? it's cool! You can also open multiple clients at the same time, and they will all receive this broadcast message. If you think this broadcast is too loud, please cancel the broadcast on the client side. In the Cancle button:
Private void btnCancle_Click (object sender, System.EventArgs e) {watch.BroadCastEvent-= new BroadCastEventHandler (wrapper.BroadCasting); MessageBox.Show ("unsubscribe broadcast succeeded!");}
Of course, at this point, the wrapper object should be declared as the private object:
Private EventWrapper wrapper = null
After the cancellation, you try to broadcast it again. Congratulations, you won't hear the noise!
Client subscribes to client events
With the previous foundation, it is much easier to look at client subscriptions to client events. And this article is written here, I am also very tired, you are also impatient by me. You're shouting, "give me a break!" As a matter of fact, this is not the case with me. So I only provide an idea, interested friends, can write their own program.
In fact, the method is very simple, similar to the second case. The client that sends the message only needs to get the remote object and send the message. The client that receives the information is responsible for subscribing to the event. Because events are placed in remote objects, the subscription method is no different from the second case!
In a special case, we can use the third case instead of the second one. As long as you put the client that sends the message to the server. Of course, some extra work needs to be done, and friends who are interested can do it. In my sample program, we have simulated the broadcast on the server side with this method, which you can take a look at.
Fourth, one point to add
In the previous event handling, I used the default EventArgs. If you want to define your own EventArgs, it's different. Because this information is value-passing serialization, [Serializable] must be added and must be placed in a public assembly and deployed to the server and the client. For example:
[Serializable] public class BroadcastEventArgs:EventArgs {private string msg = null; public BroadcastEventArgs (string message) {msg = message;} public string Message {get {return msg;}}
5. Continuous improvement (reminded by Beta, I improved my program and revised the article on December 13, 2004)
Perhaps the careful reader has noticed that in my remote object class and EventWrapper class, I commented out the attribute [OneWay] that triggered the event method. I have seen a lot of materials that say that when handling events in Remoting, the method that triggers the event must have this Attribute. What on earth is the use of this attribute?
When sending an event message, the subscriber of the event triggers the event and then responds to the event. But what happens when the subscriber of the event makes an error? For example, when you send an event message, you find that there is no event subscriber at all; or the subscriber of the event fails, such as a power outage or an abnormal shutdown. At this point, the sending party of the event will have an exception because it cannot find the correct event subscriber. Take my program as an example. When we open the server and client programs respectively, the broadcast message is normal. However, when we close the client, the exception occurs because the client does not unsubscribe. The message is as shown in the figure:
(for some reason, this exception is the same as that that occurs on the server side of a client connection. This is extremely easy for people to misunderstand. )
If we open multiple clients at the same time, other clients will not be able to receive the broadcast message because of the error caused by the shutdown of this client. So let's make the first step of improvement:
1. Consider the normal situation first. In my client, although the operation of unsubscribing is provided, it does not take into account the situation in which the user closes the client. That is, when you close the client, you do not unsubscribe to the event, so we should write in the close client form:
Private void ClientForm_Closing (object sender, System.ComponentModel.CancelEventArgs e) {watch.BroadCastEvent-= new BroadCastEventHandler (wrapper.BroadCasting);}
2. This alone is not enough. What if the client is not shut down normally, but is shut down because of a sudden power outage? At this point, the client has not had time to unsubscribe from the event. In this case, we need to use OneWayAttribute.
As mentioned earlier, an exception occurs if the sender of the event cannot find the correct event subscriber. In other words, this event belongs to unreachable. Fortunately, OneWayAttribute happens to solve this problem. In fact, you can probably guess the meaning of the feature from its named OneWay. When an event is unreachable and cannot be sent, under normal circumstances, an exception message is returned. If you add OneWayAttribute, the sending of this event becomes one-way. If an exception occurs at this time, the system will automatically throw out the exception information. Since there is no return of abnormal information, the sender will think that the message has been sent successfully. The program runs normally, the wrong client is ignored, and the correct client can still receive the broadcast message.
Therefore, the code for the remote object should look like this:
Public event BroadCastEventHandler BroadCastEvent
IBroadCast member
Public override object InitializeLifetimeService () {return null;}
3. Final improvement
Using OneWay can solve the above problems, but it is not friendly enough. Because for the party who broadcasts the message, it is like being blindfolded, ignorant of what is happening on the client. This is not a good idea. In Ingo Rammer's book Advanced .NET Remoting, Mr. Ingo Rammer proposes a better way to check the delegate chain when sending the message. And catch the exception in the traversal of the delegate chain. A prompt message is displayed when an exception occurs in one of the delegates. Then continue to traverse the subsequent delegate, which not only ensures the prompt of the abnormal information, but also ensures that other subscribers receive the message normally. Therefore, I modified the remote object in this example, commenting out [OneWay] and modifying the BroadCastInfo () method:
/ / [OneWay] public void BroadCastingInfo (string info) {if (BroadCastEvent! = null) {BroadCastEventHandler tempEvent = null; int index = 1; / / record the index entrusted by the event subscriber, starting from 1 for convenience identification. Foreach (Delegate del in BroadCastEvent.GetInvocationList ()) {try {tempEvent = (BroadCastEventHandler) del; tempEvent (info);} catch {MessageBox.Show ("event subscriber" + index.ToString () + "error occurs and the system will cancel event subscription!"); BroadCastEvent-= tempEvent;} index++;}} else {MessageBox.Show ("event not subscribed or subscription error occurred!");}}
Let's try it out. First open the server, and then open three clients at the same time. Broadcast message:
The message is sent normally.
Then close one of the client windows and broadcast the message (note that in order to simulate a client exception, you should comment on the improved unsubscribe code in the first step in the ClientForm_Closing method. Otherwise, no exception will occur. Are you really willing to use a power outage to cause an exception?), the result is as shown in the figure:
At this point, the server reports "an error occurred in event subscriber 1 and the system will cancel the event subscription". Notice that at this time the other two clients, as before, have only two broadcast messages.
When we click the OK button in the prompt box, the broadcast is still sent:
Through such improvement, the program is more perfect, but also more robust and friendly!
This is all the content of the article "sample Analysis of Remoting event handling in Microsoft .net Remoting". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.