InBefore using remoting, you have always been concerned about its concurrent processing capabilities. I have written a small test. Two client threads access remote objects. One should be returned immediately, and the other should be returned after the thread is sleeping for 3 seconds. It is found that the remote calls of both threads are returned 3 seconds later.
Today, I read the remoting chapter in msdn and found the following records:
[Programming with. NET Framework->. NET remote processing overview-> channel-> Channel rules]
The remote object sharing channel. The remote object does not have a channel.
Since each client connection is processed in its own thread, a single channel can serve multiple clients at the same time.
As described in this section, the server assigns an independent thread to each client connection for processing. Therefore, the remoting server should support concurrent processing, it is unlikely that two threads will return after 3 seconds because of thread blocking.
So today, with sockmon 2005, we tracked the port and network usage in the remoting call and found that I was misled by the original test. Through tracking, I also learned some specific implementation details of remoting, some of my previously confused questions, such as when to create and destroy network connections, and so on.
First, let's take a look at my tests.Program:
This is a remote object. When I is an odd number, the thread returns after 3 seconds of sleep; otherwise, it returns immediately.
Public Class Testclass: marshalbyrefobject {
Public String Call ( Int I) {
Long T1 = Datetime. Now. ticks;
If (I % 2 = 1 )
System. Threading. thread. Sleep ( 3000 );
Long T2 = Datetime. Now. ticks;
Return String . Format ( " Hello [I = {0}, t = {1}] " , I, T2 - T1 );
}
}
Client, run (New clienttest (). Run ():
Class Clienttest {
Public Void Run () {
Channelservices. registerchannel ( New Tcpclientchannel ());
For ( Int I = 0 ; I < 2 ; I ++ )
{
Thread t= NewThread (NewThreadstart (TEST ));
T. Start ();
}
}
Int I = 0 ;
Private Void Test () {
Int T = I ++ ;
Testclass TC = (Testclass) activator. GetObject (
Typeof (Testclass ), " TCP: // localhost: 8086/test " );
Console. writeline (TC. Call (t ));
}
}
You can run this test program in three cases:
1. After the two threads run normally, they are almost three seconds later and return:
Hello [I = 0, T = 0] Hello [I = 1, t = 30000384]
2. Run the test program when monitoring the network with sockmon,
Messages with I = 0 are returned immediately, and messages with I = 1 are returned 3 seconds later. This result should be normal.
3. Replace the value with remotingconfiguration. Configure ("client.exe. config ").
Channelservices. registerchannel (New tcpclientchannel ());
That is to say, when using the configuration file to register the channel,
No matter whether sockmon is enabled or not, the test program results are normal, the same as 2nd cases.
These two situations are depressing. Why is the test result totally different? What does the server do? Let's take a look at the tracking data:
No. |
Object |
Thread |
Local Port |
Remote Port |
Operation |
Remarks |
1 |
Server |
4584 |
8086 |
|
Bind the port and start listening |
Occurs in channel Registration Channelservices . Registerchannel |
2 |
Client |
5132 |
2296 |
8086 |
Connect and send the request TCP: // localhost: 8086/test |
Occurs in remote method call TC. Call (t), T = 0 |
3 |
Client |
4276 |
2297 |
8086 |
Connect and send the request TCP: // localhost: 8086/test |
Occurs in remote method call TC. Call (t), t = 1 |
4 |
Server |
780 |
8086 |
2096 |
Response Message Hello [I = 0, T = 0] |
|
5 |
Client |
5132 |
2296 |
8086 |
Hello [I = 0, T = 0] |
|
6 |
Server |
780 |
8086 |
2096 |
Response Information Hello [I = 1, t = 30000384] |
|
7 |
Client |
4276 |
2297 |
8086 |
Accepted Hello [I = 1, t = 30000384] |
|
Let's take a rough look at this process. After the server registers the channel (channelservices. registerchannel), it immediately starts listening on the specified port. Then the client starts to create the first testclass. And call its remote method call (). In this case, the client selects a new port (2296) to send a communication request to the server. Then we can see that another testclass is created in the second thread (ID: 4276) and connected to the server on the new port (2297.
So far, we can see that the very important fact is that when activator. GetObject () is executed, the network is not actually accessed.
Remoting starts to process requests to the server through network communication only when remote methods are actually called. For the Sao object activated on the server, it is precisely at this time that a remote object is created (Note: Singleton creates only one object instance globally, singlecall creates an object instance for each client. In fact, not all object instances are created at this time)
Second, the client uses different ports to communicate with the server in different threads. That is to say, two network connections are enabled. While TestingCode. In this regard, there are still some questions... In addition, for the client channel, it seems that its port cannot be specified. Every time the system ignores the specified port, other ports are enabled for communication -_-
Okay. Let's continue from No. 4. The server processes the call (0) and call (1) calls respectively. When I = 0, there is no thread sleep in the middle, so the processing time is basically 0. However, I have noticed that the server uses another thread (ID: 780) to process remote calls ). That is to say, on the server side, 4584 threads are responsible for listening, while 780 threads are also used to process customer requests. In addition, for client requests on ports 2296 and 2297, the server uses the same thread for processing!
Is it possible that:
/----- Thread
Server (4584 thread): Listen ____/---- 780 thread: responsible for contacting clienta ------- \ ----- thread B
\ ---- XXX thread: responsible for contacting clientx ------/---- thread C
\ ---- Thread d
That is to say, when the main listening thread receives requests from different clients, it opens up a new communication thread to communicate with the customer. While each communication thread is only responsible for the transmission at the network layer, and is still not responsible for the actual remote processing. Instead, it opens up new multi-thread Assignment Tasks? It seems that there is such a possibility, because the advantage is to implement real concurrent processing. No matter whether the client is single-threaded or multi-threaded, requests will be handed in immediately.
Let's change the code a little and comment out testclass. // if (I % 2 = 1) in the call () method, the result will be returned after 3 seconds if call () is called.
The test result is the same as the previous one in network tracking. However, the two HELLO messages will appear almost three seconds later. This means that the two call () Methods on the server are almost simultaneously called. However, the two client requests are still carried out in a communication thread (780) at the network layer. This proves that the above conjecture may be correct. Because the threads tracked through sockmon are only socket-related threads and cannot represent the usage of all threads. It is very likely that the remote method is completed by an unknown thread.
Let's start the last test today. Open two clients at the same time. The code is modified as follows:
Public Class Testclass: marshalbyrefobject {
Public String Call () {
Return String. Format ("Hello" + This. Gethashcode (). tostring ());
}
}
Class Clienttest {
Public Void Run () {
Channelservices. registerchannel ( New Tcpclientchannel (
" Channel " + This . Gethashcode (). tostring (),
New Binaryclientformattersinkprovider ()));
Testclass TC = (Testclass) activator. GetObject (
Typeof (Testclass ), " TCP: // localhost: 8086/test " );
Console. writeline (TC. Call ());
}
}
Start a server and then execute two clients.
No. |
Object |
Thread |
Localport |
Remoteport |
Operation |
1 |
Server |
4008 |
8086 |
|
Bind the port and start listening |
2 |
Client1 |
2524 |
1174 |
8086 |
Connect and send requests |
3 |
Server |
2840 |
8086 |
1174 |
Response Information |
4 |
Client1 |
2524 |
1174 |
8086 |
Accept information |
5 |
Client2 |
2500 |
1175 |
8086 |
Connect and send requests |
6 |
Server |
2292 |
8086 |
1175 |
Response Information |
7 |
Client2 |
2524 |
1174 |
8086 |
Accept information |
We can see that this time the server is a listening thread (4008), and the two clients use different threads and ports, which is similar to the previous situation. However, this time, the server finally uses two different communication threads for Client1 and Client2. So how does remoting differentiate communication threads? I think it is based on the channel we registered. In the past, two threads of one client were all passed through the same tcpclientchannel. Therefore, the server uses the same communication thread. Currently, each client uses an independent channel, and the server uses different network connections.
To sum up, the server has a main thread responsible for listening on the port we specified when creating the serverchannel. When the client has a request, the server assigns an auxiliary communication thread and a dedicated network connection to each clientchannel. You can process multiple requests simultaneously on each channel.
By the way, this statement is also verified through network tracking:
Msdn: tcpchannel opens and caches the same number of connections as the threads that are sending requests to another server. The socket connection on the client will be closed 15-20 seconds after being inactive.
No. |
Object |
Thread |
Localport |
Remoteport |
Operation |
8 |
Client1 |
2524 |
1174 |
8086 |
Close socket and end thread |
9 |
Server |
2840 |
8086 |
1174 |
Close socket and end thread |
10 |
Client2 |
2500 |
1175 |
8086 |
Close socket and end thread |
11 |
Server |
2292 |
8086 |
1175 |
Close socket and end thread |
After 15 seconds, the communication threads of the two channels on the server and the client are terminated.