The emergence of distributed COM (hereinafter referred to as DCOM) provides us with the opportunity to easily create distributed applications; we can completely ignore low-level Windows Sockets (DCOM allows customers to communicate with objects through MS-RPC, fortunately, To develop COM applications, developers can almost ignore MS-RPC) and develop powerful functions and low coupling (functional modules are relatively independent, It has made good use of OO's ideas) and is easy to deploy distributed computing system.
In this article, we intend to use DCOM to develop a LAN chat room, not only as a technical research, but actually I believe this should also be a useful tool. First of all, we need to have a general understanding of the functions of this chat room:
1. At least this chat room should allow multiple LAN users to chat.
2. There should be sub-chat rooms with multiple topics, and users can choose to enter a chat room to chat.
3. The client should be as simple as possible (without configuring DCOM), and a server side needs to manage all interaction behaviors, manage the number of chat rooms and related configurations, and do a good job in system monitoring and logging.
4. Expand the chat room functions (such as quiet conversation functions, emojis, etc.). Based on the above functional description, after carefully analyzing the problem, we designed the following sketch:
In this article, we want to roughly implement a basic core of this program, including IChatManager, TChatRoomManager, and TchatRoom, to complete the server with the most basic functions and to perform a simple client detection. Our focus is on the server side, because it will implement most of the functions of the chat room, and the client is just a very small and simple program.
Due to space, we only list the code for important parts. Please email me for the complete program. First, let’s take a look at what our IchatManager interface looks like:
IChatManager = interface(IDispatch)
['{E7CD7F0D-447F-497A-8C7B-1D80E748B67F}']
PRocedure SpeakTo(const content: WideString; destid: Integer); safecall;
//The client speaks to the designated room, and the destid is the room number
function ReadFrom(sourceid: Integer): IStrings; safecall;
//The client reads the conversation content from the designated room, the sourceid is the room number
function ReadReady(id: Integer): Byte; safecall;
//The customer can check whether the specified room can already read the conversation content
procedure ConnectRoom(const UserName: WideString; RoomID: Integer); safecall;
//Customer logs into designated rooms
procedure DisconnectRoom(const UserName: WideString; RoomID: Integer); safecall;
//The customer exits the designated room
function TestClearBufferTag(RoomID: Integer): Integer; safecall;
//Customer tests whether the buffer zone of the specified room is cleared or not
end;
Let’s take a look at the TChatManager section of the interface implementation class:
type
TChatManager = class(TAutoObject, IChatManager)
protected
function ReadFrom(sourceid: Integer): IStrings; safecall;
// Here we use the complex type TStings extended by Delphi, in order to make COM support this
//Type, delphi provides IStrings interface
procedure SpeakTo(const content: WideString; destid: Integer); safecall;
function ReadReady(id: Integer): Byte; safecall;
// Used to provide the client to query whether the specified room is readable, and whether the specified room buffer is empty
procedure ConnectRoom(const UserName: WideString; RoomID: Integer);
safecall;
procedure DisconnectRoom(const UserName: WideString; RoomID: Integer);
safecall;
function TestClearBufferTag(RoomID: Integer): Integer; safecall;
end;
Implementation part:
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(sourceid);
while TempRoom.Locked do
Begin
//do nothing is just waiting to be unlocked
end;
GetOleStrings(TempRoom.OneRead,Result);
end;
procedure TChatManager.SpeakTo(const content: WideString; destid: Integer);
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(destid);
while TempRoom.Locked do
Begin
//do nothing is just waiting to be unlocked
end;
TempRoom.OneSpeak(content);
end;
function TChatManager.ReadReady(id: Integer): Byte;
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(id);
if TempRoom.CanRead then result:=1 else Results:=0;
end;
procedure TChatManager.ConnectRoom(const UserName: WideString;
RoomID: Integer);
//The client logs in to the specified room through the interface, and is not fully implemented
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
TempRoom.LoginRoom(UserName);
end;
procedure TChatManager.DisconnectRoom(const UserName: WideString;
RoomID: Integer);
//The client leaves the specified room through the interface and is not fully implemented
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
TempRoom.LeaveRoom(UserName);
end;
function TChatManager.TestClearBufferTag(RoomID: Integer): Integer;
var
TempRoom:TChatRoom;
Begin
TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
result:=TempRoom.ClearBufferTag;
end;
Initialization
TAutoObjectFactory.Create(ComServer, TChatManager, Class_ChatManager,
ciMultiInstance, tmApartment);
end.
The most important TchatRoom looks like:
type
TChatRoom=class
Private
FBuffer:array[1..20] of string;
FBufferLength:integer;
FRoomName:string;
FRoomID:integer;
FLocked:boolean;//Synchronous lock, used to handle the situation where multiple people send out conversations at the same time
FConnectCount:integer;//The current number of people in the room
FClearBufferTag:integer;
//This value will jump once every time the buffer is cleared, and this pulse is detected by the client
protected
procedure ClearBuffer;//Clear the buffer
function GetCanRead:boolean;
public
constrUCtor Create(RoomName:string;RoomID:integer);
procedure OneSpeak(content:string);//Add a chat content to the buffer
procedure LoginRoom(UserName:string);//Refer to the implementation part of the comments
procedure LeaveRoom(UserName:string);//Refer to the implementation part of the comments
function OneRead:Tstrings;//Read the record from the buffer
property Locked:boolean read FLocked; //readonly;//For IChatManager detection
property CanRead:boolean read GetCanRead;//Determine whether the buffer is empty, otherwise it is unreadable
property ClearBufferTag:integer read FClearBufferTag;
end;
TchatRoom implementation:
{ TChatRoom }
constructor TChatRoom.Create(RoomName:string;RoomID:integer);
Begin
FBufferLength:=0;
FConnectCount:=0;
FClearBufferTag:=1;
FLocked:=false;
FRoomName:=RoomName;
FRoomID:=RoomID;
end;
procedure TChatRoom.ClearBuffer;
var
i:integer;
Begin
/// Here you can detect a flag to determine whether the server needs to record each chat content.
for i:=1 to 20 do
FBuffer[i]:='';
FBufferLength:=0;
FClearBufferTag:=0-FClearBufferTag;
end;
procedure TChatRoom.OneSpeak(content:string);
Begin
FLocked:=true;
inc(FBufferLength);
if FBufferLength>20 then
Begin
ClearBuffer;
inc(FBufferLength);
end;
FBuffer[FBufferLength]:=content;
FLocked:=false;
end;
function TChatRoom.OneRead:TStrings;
var
FStrings:TStrings;
i:integer;
Begin
FLocked:=true;
FStrings:=TStringList.Create;
for i:=1 to FBufferLength do
FStrings.Add(FBuffer[i]);
result:=FStrings;
FLocked:=false;
end;
function TChatRoom.GetCanRead: boolean;
Begin
result:=false;
if FBufferLength>0 then result:=true;
end;
procedure TChatRoom.LoginRoom(UserName:string);
//The user login chat room event is not fully implemented here
Begin
inc(FConnectCount);
end;
procedure TChatRoom.LeaveRoom(UserName: string);
//The user leaves the chat room event, it is not fully implemented here
Begin
Dec(FConnectCount);
end;
The last important part of the server side TchatRoomManager:
type
TChatRoomManager=class
Private
ChatRoom:array of TChatRoom;
public
constructor Create;
function FindRoomByID(id:integer):TChatRoom;
end;
Implementation part:
{ TChatRoomManager }
constructor TChatRoomManager.Create;
var
i,RoomCount:integer;
RoomNames:TStrings;//RoomName is the chat room name in the configuration file
Begin
RoomCount:=1;
// Here we will read out several chat rooms from the configuration file.
RoomNames:=TStringList.Create;
RoomNames.Add('TestRoom');//This sentence will be replaced by the final read from the configuration file.
setlength(ChatRoom,RoomCount);
for i:=1 to RoomCount do
ChatRoom[i]:=TChatRoom.Create(RoomNames[i-1],i);
end;
function TChatRoomManager.FindRoomByID(id:integer): TChatRoom;
//This function is called by the IChatManager interface, since the final version of the interface will be provided to the customer
//The function of getting the room list is so the client knows the id of its room
Begin
result:=ChatRoom[id];
end;
Initialization
ChatRoomManager:=TChatRoomManager.Create;
end.
After the main core part of the server side is completed, we configure the server-side DCOM configuration and can develop a simple client for testing: (Although the client is as simple as possible, we do not need to configure DCOM, but we still need to copy the server-side type The library file .tlb can only be developed and used after registering the client. Of course, these can be done through the installer)
On the client we only list two relatively important functions, and the rest are omitted. Please think I'll write to get all the programs:
procedure TForm1.Button1Click(Sender: TObject);
//Click button1 and "say" the content of edit
Begin
Server.SpeakTo(edit1.Text,1);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
//Request the conversation content from the server every once in a while, and I set it to 1.5 seconds
var
TempStrings:TStrings;
i:integer;
Begin
if Server.ReadReady(1)=1 then
Begin
TempStrings:=TStringList.Create;
SetOleStrings(TempStrings,Server.ReadFrom(1));
if FReadStartPos>19 then
if (FClearBufferTag=0-Server.TestClearBufferTag(1)) then
Begin
FReadStartPos:=0;
FClearBufferTag:=Server.TestClearBufferTag(1);
end;
for i:=FReadStartPos to TempStrings.Count-1 do
Memo1.Lines.Add(TempStrings[i]);
FReadStartPos:=TempStrings.Count;
end;
end;
The core part of a DCOM-based LAN chat room has been basically completed, and all the tests have been relatively smooth. Here I need to add a difficulty of the chat room server: the developer needs to handle synchronization very carefully, although I have also done it. There is a certain amount of synchronization processing, but deadlocks or other live locks may still occur when there are many clients. This program needs further testing and even a certain amount of reconstruction.