All developers who use delphifor multi-layer development should be familiar with scktsrvr.exe,
Borland provided its source code in Delphi.
This is a 900-line program, the program is not long,
Now I only select some of them to read them carefully.
The line is roughly the connection from the server to the client to process a request from the client (here
Select the 'retrieve Application Server list' request sent from the client to the server)
After the Server accepts the client connection,
Because serversocket uses the blocking mode, the server executes the following thread
Service client:
// Scktmain. Pas
Procedure tsocketdispatcherthread. clientexecute;
VaR
Data: idatablock;
MSG: tmsg;
OBJ: isenddatablock;
Event: thandle;
Waittime: DWORD;
Begin
Coinitialize (NiL); // initialize com
Try
Synchronize (addclient); // displays the customer information on the program interface,
// Use synchronization to ensure the safety of addclient threads
Ftransport: = createservertransport;
Try
Event: = ftransport. getwaitevent;
Peekmessage (MSG, 0, wm_user, wm_user, pm_noremove );
Getinterface (isenddatablock, OBJ );
If fregisteredonly then
Finterpreter: = tdatablockinterpreter. Create (OBJ, ssockets) else
Finterpreter: = tdatablockinterpreter. Create (OBJ ,'');
Try
OBJ: = nil;
If ftimeout = 0 then
Waittime: = infinite else
Waittime: = 60000;
While not terminated and ftransport. Connected do
Try
Case msgwaitformultipleobjects (1, event, false, waittime, qs_allevents)
// Msgwaitformultipleobjects is used to maintain thread synchronization,
// This article does not detail it for the moment.
Wait_object_0: // data is returned.
Begin
Wsaresetevent (event );
Data: = ftransport. Receive (false, 0); // receives data blocks from the client
If assigned (data) then
Begin
Flastactivity: = now;
Finterpreter. interpretdata (data); // next, analyze it here
Data: = nil;
Flastactivity: = now;
End;
End;
Wait_object_0 + 1:
While peekmessage (MSG, 0, 0, 0, pm_remove) Do
Dispatchmessage (MSG );
Wait_timeout:
If (ftimeout> 0) and (now-flastactivity)> ftimeout) then
Ftransport. Connected: = false;
End;
Except
Ftransport. Connected: = false;
End;
Finally
Finterpreter. Free;
Finterpreter: = nil;
End;
Finally
Ftransport: = nil;
End;
Finally
Couninitialize;
Synchronize (removeclient );
End;
End;
That's why we are comfortable with the 60 s.
Apart from the process control statements, we mainly see two things:
Ftransport and finterpreter ).
In this article, I am more interested in the implementation of its application protocol,
Data transmission (ftransport) is first put aside, and then again.
Now let's take a look at its data parsing section.
...
Finterpreter: = tdatablockinterpreter. Create (OBJ, ssockets) else
Finterpreter: = tdatablockinterpreter. Create (OBJ ,'');
// The differences between the two methods for creating tdatablockinterpreter class instances do not matter.
...
Finterpreter. interpretdata (data );
...
That's it.
What exactly does it do ??
======================
...
Type
Tsocketdispatcherthread = Class (tserverclientthread, isenddatablock)
Private
...
Finterpreter: tdatablockinterpreter;
Ftransport: itransport;
...
======================
Finterpreter this object reference is defined here.
Procedure tdatablockinterpreter. interpretdata (const data: idatablock );
VaR
Action: integer;
Begin
Action: = data. Signature; // retrieves the request flag value from the data block sent from the client.
// The specific data format of the client data block.
If (action and asmask) = aserror then doexception (data );
Try
Case (action and asmask) of // process the request based on the request flag value of the client.
// Oh, there are only a few. A large Midas System
// The whole site is on their shoulders.
Asinvoke: doinvoke (data );
Asgetid: dogetidsofnames (data );
Ascreateobject: docreateobject (data );
Asfreeobject: dofreeobject (data );
Asgetservers: dogetserverlist (data );
Asgetappservers: dogetappserverlist (data); // read this and read it again.
Else
If not docustomaction (action and asmask, data) then
Raise einterpretererror. createresfmt (@ sinvalidaction, [Action and asmask]);
End;
Except
On E: exception do
Begin
Data. Clear;
Writevariant (E. Message, data );
Data. Signature: = resultsig or aserror;
Fsenddatablock. Send (data, false );
End;
End;
End;
======================================
Step by step,
View how the appserver list is retrieved and returned to the client.
It is easy to use getmidasappserverlist to retrieve the local Midas application service.
Server LIST, write it in data, and send it back to the client.
======================================
Procedure tdatablockinterpreter. dogetappserverlist (const data: idatablock );
VaR
Vlist: olevariant;
List: tstringlist;
I: integer;
Begin
Data. Clear;
List: = tstringlist. Create;
Try
Getmidasappserverlist (list, fcheckregvalue); // retrieve the list of application servers on the local machine
// Do you want to know how it works?
// You can view the source code.
If list. Count> 0 then
Begin
Vlist: = vararraycreate ([0, list. Count-1], varolestr );
For I: = 0 to list. Count-1 do
Vlist [I]: = list [I];
End else
Vlist: = NULL;
Finally
List. Free;
End;
Writevariant (vlist, data );
Data. Signature: = resultsig or asgetappservers;
Fsenddatablock. Send (data, false); // return the application server list to the client.
End;
========================================================== ======================
Oh... There is another thing I haven't mentioned above. It's the mysterious data format, which is an interface
Provided in the form,
This program uses the interface implemented in tdatablock.
The source code shows that tdatablock contains a stream,
Function tdatablock. getsize: integer;
Begin
Result: = fstream. Size-bytesreserved; // The value of bytesreserved is 8.
End;
From this we can see that the data block is counted from the 8th bytes of stream, and the first is the position of two int-type numbers.
Function tdatablock. getsignature: integer;
Begin
Fstream. Position: = 0;
Fstream. Read (result, sizeof (result ));
End;
Well, that's right. The first four bytes of stream are its signature. The request mark of the client is put here.
Function tdatablock. getstream: tstream;
VaR
Datasize: integer;
Begin
Fstream. Position: = 4;
Datasize: = fstream. Size-bytesreserved;
Fstream. Write (datasize, sizeof (datasize ));
Fstream. Position: = 0;
Result: = fstream;
End;
It is obvious that the data contains the Data Length value.
It should be mentioned that, if you want to change the format of the data block for transmission, for example, encrypt or pressurize it,
You must implement the idatablock interface on your own. Replace tdatablock.
From these source codes, we can get a lot of things, regardless of the elegant style,
Whether it is communication program development, the middleware database, or DCOM learning or application,
It is a source program worth reading.
I think, more importantly, it provides a rigorous, elegant, and practical example.
A flexible and practical framework system.
Halfdream (yawning) On the evening of 2001-10-14