求indy的类似聊天室的问题(200分)

  • 主题发起人 主题发起人 soketjjkjdk
  • 开始时间 开始时间
S

soketjjkjdk

Unregistered / Unconfirmed
GUEST, unregistred user!
要写一个类似聊天室的例子,多个客户端与服务段建立连接,客户端使用同一IP的不同端口连接服务端,或者,客户端采用不同IP连接。然后再服务端给出一个客户端的连接端口,根据这个端口,服务端向客户端发送消息,类似于聊天室的私聊得编写。初学,不了解该如何实现,还请各位大侠不吝指教。最好是使用INDY。
 
这个是indy使用tcp方式做的demo,
可以实现你的功能。
我觉得你可以下载一个indy的demo包看看。网上很多。
{-----------------------------------------------------------------------------
Demo Name: ServerFrmMainUnit
Author: Helge Jung (helge@eco-logic-software.de)
Copyright: Indy Pit Crew
Purpose:
History: Improvements supplied by: Enver ALTIN
Date: 27/10/2002 00:23:25
Checked with Indy version: 9.0 - Allen O'Neill - Springboard Technologies Ltd - http://www.springboardtechnologies.com
-----------------------------------------------------------------------------
Notes:

Demonstration on how to use TIdTCPServer and TIdTCPClient
with using Threads and WriteBuffer/ReadBuffer

}

unit ServerFrmMainUnit;

interface

uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,
StdCtrls,IdTCPServer,IdThreadMgr,IdThreadMgrDefault,IdBaseComponent,
IdComponent;

type
PClient=^TClient;
TClient=record // Object holding data of client (see events)
DNS:string[20]; { Hostname }
Connected, { Time of connect }
LastAction:TDateTime; { Time of last transaction }
Thread:Pointer; { Pointer to thread }
end;

TServerFrmMain=class(TForm)
Server:TIdTCPServer;
CBServerActive:TCheckBox;
Protocol:TMemo;
IdThreadMgrDefault1:TIdThreadMgrDefault;

procedure CBServerActiveClick(Sender:TObject);
procedure ServerConnect(AThread:TIdPeerThread);
procedure ServerExecute(AThread:TIdPeerThread);
procedure ServerDisconnect(AThread:TIdPeerThread);
procedure FormCreate(Sender:TObject);
procedure FormClose(Sender:TObject; var Action:TCloseAction);

private

public
end;

var
ServerFrmMain:TServerFrmMain;
Clients:TThreadList; // Holds the data of all clients

implementation

uses GlobalUnit;

{$R *.DFM}

procedure TServerFrmMain.CBServerActiveClick(Sender:TObject);
begin
Server.Active:=CBServerActive.Checked;
end;

procedure TServerFrmMain.ServerConnect(AThread:TIdPeerThread);
var
NewClient:PClient;

begin
GetMem(NewClient,SizeOf(TClient));

NewClient.DNS:=AThread.Connection.LocalName;
NewClient.Connected:=Now;
NewClient.LastAction:=NewClient.Connected;
NewClient.Thread:=AThread;

AThread.Data:=TObject(NewClient);

try
Clients.LockList.Add(NewClient);
finally
Clients.UnlockList;
end;

Protocol.Lines.Add(TimeToStr(Time)+' Connection from "'+NewClient.DNS+'"');
end;

procedure TServerFrmMain.ServerExecute(AThread:TIdPeerThread);
var
ActClient,RecClient:PClient;
CommBlock,NewCommBlock:TCommBlock;
RecThread:TIdPeerThread;
i:Integer;

begin
if not AThread.Terminated and AThread.Connection.Connected then
begin
AThread.Connection.ReadBuffer(CommBlock,SizeOf(CommBlock));
ActClient:=PClient(AThread.Data);
ActClient.LastAction:=Now; // update the time of last action

if (CommBlock.Command='MESSAGE')or(CommBlock.Command='DIALOG') then
begin // 'MESSAGE': A message was send - forward or broadcast it
// 'DIALOG': A dialog-window shall popup on the recipient's screen
// it's the same code for both commands...

if CommBlock.ReceiverName='' then
begin // no recipient given - broadcast
Protocol.Lines.Add(TimeToStr(Time)+' Broadcasting '+CommBlock.Command+': "'+CommBlock.Msg+'"');
NewCommBlock:=CommBlock; // nothing to change ;-))

with Clients.LockList do
try
for i:=0 to Count-1 do // iterate through client-list
begin
RecClient:=Items; // get client-object
RecThread:=RecClient.Thread; // get client-thread out of it
RecThread.Connection.WriteBuffer(NewCommBlock,SizeOf(NewCommBlock),True); // send the stuff
end;
finally
Clients.UnlockList;
end;
end
else
begin // receiver given - search him and send it to him
NewCommBlock:=CommBlock; // again: nothing to change ;-))
Protocol.Lines.Add(TimeToStr(Time)+' Sending '+CommBlock.Command+' to "'+CommBlock.ReceiverName+'": "'+CommBlock.Msg+'"');
with Clients.LockList do
try
for i:=0 to Count-1 do
begin
RecClient:=Items;
if RecClient.DNS=CommBlock.ReceiverName then // we don't have a login function so we have to use the DNS (Hostname)
begin
RecThread:=RecClient.Thread;
RecThread.Connection.WriteBuffer(NewCommBlock,SizeOf(NewCommBlock),True);
end;
end;
finally
Clients.UnlockList;
end;
end;
end
else
begin // unknown command given
Protocol.Lines.Add(TimeToStr(Time)+' Unknown command from "'+CommBlock.MyUserName+'": '+CommBlock.Command);
NewCommBlock.Command:='DIALOG'; // the message should popup on the client's screen
NewCommBlock.MyUserName:='[Server]'; // the server's username
NewCommBlock.Msg:='I don''t understand your command: "'+CommBlock.Command+'"'; // the message to show
NewCommBlock.ReceiverName:='[return-to-sender]'; // unnecessary

AThread.Connection.WriteBuffer(NewCommBlock,SizeOf(NewCommBlock),True); // and there it goes...
end;
end;
end;

procedure TServerFrmMain.ServerDisconnect(AThread:TIdPeerThread);
var
ActClient:PClient;

begin
ActClient:=PClient(AThread.Data);
Protocol.Lines.Add(TimeToStr(Time)+' Disconnect from "'+ActClient^.DNS+'"');
try
Clients.LockList.Remove(ActClient);
finally
Clients.UnlockList;
end;
FreeMem(ActClient);
AThread.Data:=nil;
end;

procedure TServerFrmMain.FormCreate(Sender:TObject);
begin
Clients:=TThreadList.Create;
end;

procedure TServerFrmMain.FormClose(Sender:TObject; var Action:TCloseAction);
begin
Server.Active:=False;
Clients.Free;
end;

end.
 
我写过,用一个Socketserver和n个socketclient就搞定了。简单快捷
 
to:hhjjhhjj,谢谢,这方法在这里不行。
to:zywcd,
多谢,indy的DEMo不过看不太明白。
你这个例子我不太理解
try
for i:=0 to Count-1 do
begin
RecClient:=Items;
if RecClient.DNS=CommBlock.ReceiverName then // we don't have a login function so we have to use the DNS (Hostname)
begin
RecThread:=RecClient.Thread;
RecThread.Connection.WriteBuffer(NewCommBlock,SizeOf(NewCommBlock),True);
end;
end;
我想我想要的应该是在这里, for i:=0 to Count-1 do RecClient:=Items;
这样每次下发使都要搜索一遍所有的连接,这样的话我感觉效率太差,有没有办法通过一个列表记录IdThreadMgrDefault1的Count和发起IP,通过搜索这个列表直接确定这个RecClient:=Items中的I呢?不要用遍历的方法。而且,只能通过知道客户端的发起IP或者端口来确定发送目的。
 
"搜索这个列表"也是一个遍历啊,不要相信感觉.
 
我看过一本书上说明,这样向里插数据的话,那样调用 RecThread.Connection如果不加详细的判断,athread.data中现在有没有数据发送,数据处理中有没有使用循环,可能会引起进程中的for循环中断等状况,中间的数据处理比较多,所以我不希望频繁的lock和unlock。而且除了本身就有个Tlist。
 
这个demo是做聊天用的。这样的目的是保存所有连接的用户的连接信息,包括对方的计算机名称,ip ,端口。如果你想做聊天软件,必须保存这些信息,而且必须遍历吧,否则怎么找啊?
还有,这个软件需要客户端一致保持连接。
 
后退
顶部