The legendary WCF (1): Is this hard to learn?The legendary WCF (2): What about service agreements?Legendary WCF (3): multiple protocolsLegendary WCF (4): send and receive SOAP HeadersLegendary WCF (5): data protocols ()Legendary WCF (6): data protocols (B)Legendary WCF (7): "one-way" & "two-way"Legendary WCF (8): Play with message protocolsLegendary WCF (9): stream and file transferLegendary WCF (10): message interception and tamperingThe legendary WCF (11): Session)The legendary WCF (12): What is the use of server callback?Legendary WCF (13): group chat programThe legendary WCF (14): WCF can also be used as a chat program.
In the title, I added a word that everyone is familiar with-session. Are you familiar with it? Those who have played web development will surely see her in their dreams.
Why do we need to talk on the web? After all, each user may perform more than one operation in a web application. For example, a user a may modify his or her personal information after logging on to a second-hand aircraft transaction website, he may have watched a second-hand plane and planned to put the goods in his "Shopping Cart". In these processes, many data related to user A will be generated, this data is only valid for user A. After user B logs on to user B, he or she will have his or her own data,Each client has an independent data storage zone on the server, which is unrelated to each other. It is like a talking with the server separately..
In WCF, the meaning of a session is similar to that of a web session, that is, the client and the server are in a "private chat", which means there is a session call, A call without a session is called "group chat". During communication, data is displayed in plain text without encryption. This is called "Bare chat ".
Now, you must understand the session. However, a session is invisible. How can we test it through an instance?
Next, let's write an example to see what happens when two associated codes are continuously called on the binding that does not support sessions.
[Servicecontract]
Public interface iservice
{
[Operationcontract (isoneway = true)]
Void setvalue (int n );
[Operationcontract]
Int getvalue ();
}
[Servicebehavior (includeexceptiondetailinfaults = true)]
Public class myservice: iservice
{
Public myservice ()
{
Console. writeline ("-------------------------------");
Console. writeline ("{0}-the service is instantiated. ", Datetime. Now. tolongtimestring ());
}
// Output information in the destructor
~ Myservice ()
{
Console. writeline ("{0}-the service instance is released. ", Datetime. Now. tolongtimestring ());
Console. writeline ("--------------------------------");
}
/// <Summary>
/// Private field
/// </Summary>
Private int m_value = int. minvalue;
Public void setvalue (int n)
{
This. m_value = N;
Console. writeline ("session ID: {0}", operationcontext. Current. sessionid );
}
Public int getvalue ()
{
Console. writeline ("session ID: {0}", operationcontext. Current. sessionid );
Return this. m_value;
}
}
In the service class, we output some content in the constructor and destructor respectively, so that we can see at runtime when the service is instantiated and when it is released. At the same time, when calling each operation agreement, we also output the ID of the current session. If it is null, it indicates that the session is not enabled currently.
Next, we use unsupported bindding to implement the service host.
Static void main (string [] ARGs)
{
Console. Title = "WCF server ";
Using (servicehost host = new servicehost (typeof (myservice), new uri ("http: // 127.0.0.1: 1211/SV ")))
{
// Bind
Basichttpbinding binding = new basichttpbinding ();
Binding. Security. mode = basichttpsecuritymode. None; // No security mode is required.
Host. addserviceendpoint (typeof (iservice), binding, "/EP ");
// Service metadata
Servicemetadatabehavior MB = new servicemetadatabehavior ();
MB. httpgetenabled = true;
MB. httpgeturl = new uri ("http: // 127.0.0.1: 8008/meta ");
Host. description. behaviors. Add (MB );
Host. Opened + = (sender, ARG) =>
{
Console. writeline ("service started. ");
};
Try
{
Host. open (); // open the service
Console. Read ();
}
Catch (exception ex)
{
Console. writeline (ex. Message );
}
}
}
Then, implement the client. In order to make the demo more convenient, the client uses the Windows Forms project. The interface is roughly shown in. The source code will be uploaded to the resource later.
The Service has two operations. from the definition of the service class, we know that I have defined a private field in the service class, and the two operation protocols published are set and get the value respectively.
Public partial class form1: Form
{
WS. serviceclient client = NULL;
Public form1 ()
{
Initializecomponent ();
Client = new ws. serviceclient ();
// Close the channel
This. formclosing + = (frmsender, frmargs) => client. Close ();
}
Private void btncall1_click (Object sender, eventargs E)
{
Int V;
If (! Int.tryparse(this.txt input. Text, out V ))
{
Return;
}
Client. setvalue (v );
}
Private void btncall2_click (Object sender, eventargs E)
{
Int v = client. getvalue ();
This.txt output. Text = V. tostring ();
}
}
Now we need to test whether we can obtain the expected values by calling these two methods. That is, if I call setvalue (100), 100 should be returned when getvalue is called. Is that true?
I didn't see the expected value of Bagua Road.
I entered 100, but the min int is obtained. Let's see what information the server outputs.
Obviously, each time an operation is called, the service class is instantiated once, which means that instance a is called when setvalue is called, and instance B may be called when getvalue is called. Therefore, the private field value is not saved.
How can we prove that the two called operations do not belong to the same service instance? Do you still remember gethashcode? Yes, as long as the instance is not in the same memory, its hash value must be different. If so, let's change the above service code.
Public void setvalue (int n)
{
This. m_value = N;
Console. writeline ("------------------------");
Console. writeline ("current instance hash value: {0}", this. gethashcode (). tostring ("X "));
Console. writeline ("session ID: {0}", operationcontext. Current. sessionid );
}
Public int getvalue ()
{
Console. writeline ("------------------------");
Console. writeline ("current instance hash value: {0}", this. gethashcode (). tostring ("X "));
Console. writeline ("session ID: {0}", operationcontext. Current. sessionid );
Return this. m_value;
}
So what will happen this time? Let's look at the results.
This result confirms my previous inference that the two methods called in succession are not of the same instance.
What will happen if the session is enabled?
[Servicecontract (sessionmode = sessionmode. Required)]
Public interface iservice
{
.....
}
When defining a service agreement, you can set sessionmode so that the service requires the client to enable the session.
Next, bind sessions.
// Bind
// Basichttpbinding binding = new basichttpbinding ();
// Binding. Security. mode = basichttpsecuritymode. None; // No security mode is required.
// Host. addserviceendpoint (typeof (iservice), binding, "/EP ");
Nettcpbinding binding = new nettcpbinding ();
Binding. Security. mode = securitymode. None;
Host. addserviceendpoint (typeof (iservice), binding, "net. TCP: // 127.0.0.1: 2377/EP ");
Update the service reference of the client. Then, let's see if this will achieve our goal.
How are you doing? Finally, we can see what we want. I input 100 and the result is 100. This time, from the server output, the service class is instantiated only once, the two hash values are the same, which proves that they are indeed the same instance. At the same time, we also see that the session IDs of the two calls are the same, which also shows that, the two client calls are based on the same session. As a result, 100 of the incoming calls can be retrieved smoothly.
You may wish to open a few more clients to try.
What are the different session IDs, hash values, and instantiation time? This indicates:The server independently maintains sessions with each client..
Next we need to modify our solution.
On the server side, change the service agreement:
[Servicecontract (sessionmode = sessionmode. Required)]
Public interface iservice
{
[Operationcontract (isoneway = true, isinitiating = true, isterminating = false)]
Void setvalue (int n );
[Operationcontract]
Int getvalue ();
[Operationcontract (isinitiating = false, isterminating = true)]
Void endsession ();
}
Added the implementation of the endsession method in the service class.
Public void endsession ()
{
Console. writeline ("session ends. ");
}
Have you seen the changes?
When we use operationcontractattribute to define the operation agreement, we set two attributes:
A. isinitiating: if it is true, the session is enabled when this operation is called.
B. isterminating: if it is true, the session is terminated when the operation is used.
Therefore, the preceding example indicates that the session starts when setvalue is called. When the endsession method is called and the session ends, it is best to use the method with the return value of void or one-way communication (one way). In this way, you do not have to wait for the customer to end the session. Because of one-way communication, you do not need to reply to the client, it can terminate the session immediately after being called.
WS. serviceclient client = NULL;
Public form1 ()
{
Initializecomponent ();
Client = new ws. serviceclient ();
// Close the channel
// This. formclosing + = (frmsender, frmargs) => client. Close ();
}
Then, the endsession will be ready immediately after the getvalue is called to see what will happen this time.
In this way, once the three methods are called, the session ends and the service instance is liberated.
Next,Isinitiating = true to start a new session, and isterminating = true to end the session.
Turn to it