您的位置:首页 > 理论基础 > 计算机网络

Delphi中关于idtcpserver的使用解答(转)

2013-08-12 16:20 411 查看

Delphi中关于idtcpserver的使用解答(转)

用idTCPServer客户端接上来时,如何取得客户端的IP?
IP:=AThread.Connection.Binding.PeerIP;

Port:=AThread.Connection.Binding.PeerPort;

尝试解答你的疑问:

问题一:

在Form1中放入IDTCPServer控件,一旦有socket连接,IDTCPServer自动建立一个线程与之

建立一个TCP/IP连接,我们在IDTCPServer.OnExecute中写入自己的代码就可以在这个独立

的线程中完成我们所希望的动作吗?

解答:

一旦有socket连接,IDTCPServer
不仅建立一个线程,更需要把这个建立的线程保存到一个

线程列表中去。然后在 IDTCPServer.OnExecute 中传入“每线程”这个参数,程序从传入

的“每线程”这个参数,检索出对应的 socket 连接。
问题二:

如果我们在OnExecute中调用TForm1.aaa这个函数,那么这个函数是不是会造成同步问题,

例如登录人数的统计。

解答:

统计登录人数不应该在这个事件中处理。其实只要读一下线程列表就可以知道结果。OnExecute

中的同步,是 indy 的一个工作要求,同时发生的客户必须排队处理。所以,原则上不会

造成同步问题,但是,如果你引用的 Form 过程中,有异步变量,就要注意可能的同步问题。
不知道这样的回答,是否能让你满意。

您是不是不要这样理解:

“FForm.IdTCPClient1.Connected then // IdTCPClient1 在 FForm 这个主线程中”

您可以理解为:IdTCPClient1 是类实例 FForm 的一个成员。至于您的 TReceiveThread

中引用了 FForm 这个类,并把这个 FForm 类做为了 TThread 类的成员就值得考虑了。

按你描述的意思,大概这个 TfmClient 是一个 TForm 类,这时候除非你动态在线程里创建

这个 TfmClient 类,不然的话就可能有苦头吃了,很容易造成死锁。

已经说了,Indy 是一个多线程控件,在 Server 连接的时候,针对每客户会创建一个线程,

只要有客户发送数据,就会激活 Srever 的 OnExecute 事件。需要做的,就是在 OnExecute

中识别是哪个客户(也即线程)发来的请求,针对这个客户的 socket 连接返回服务就可以

了。

Server 端首先是响应客户的 Connect 事件,一旦连接了,就自动在服务端建立了一个连接

线程。而这个连接线程是需要 Server 维护的,indy 的最大连接线程数不会大于 600 个,

有 600 个线程你还不够用的话,基本上就不能使用 indy 控件了。

Event handler for peer thread execution.
property OnExecute: TIdServerThreadEvent;
Description
OnExecute is an event handler for TIdServerThreadEvents. OnExecute occurs when a TIdPeerThread attempts to perform the
TIdPeerThread.Run method. OnExecute receives AThread as a parameter, representing the TIdPeerThread thread that will be
started.
Assign a TIdServerThreadEvent event handler procedure to OnExecute to respond to the event notification.
Use CommandHandlers and CommandHandlersEnabled to provide finer control over commands executed for a peer thread connection.

-----

procedure TIdListenerThread.Run;

var

LIOHandler: TIdIOHandler;

LPeer: TIdTCPServerConnection;

LThread: TIdPeerThread;

begin

try

if Assigned(Server) then begin // This is temporary code just to test one exception

while True do begin

LThread := nil;

LPeer := TIdTCPServerConnection.Create(Server);

LIOHandler := Server.IOHandler.Accept(Binding.Handle, SELF);

if LIOHandler = nil then begin

FreeAndNil(LPeer);

Stop;

Exit;

end

else begin

LThread := TIdPeerThread(Server.ThreadMgr.GetThread);

LThread.FConnection := LPeer;

LThread.FConnection.IOHandler := LIOHandler;

LThread.FConnection.FFreeIOHandlerOnDisconnect := true;

end;
// LastRcvTimeStamp := Now; // Added for session timeout support

// ProcessingTimeout := False;

if (Server.MaxConnections > 0) and // Check MaxConnections

NOT TIdThreadSafeList(Server.Threads).IsCountLessThan(Server.MaxConnections)

then begin

Server.ThreadMgr.ActiveThreads.Remove(LThread);

LPeer.WriteRFCReply(Server.MaxConnectionReply);

LPeer.Disconnect;

FreeAndNil(LThread); // This will free both Thread and Peer.

end else begin

Server.Threads.Add(LThread); //APR

// Start Peer Thread

LThread.Start;

Break;

end;

end;

end;

except

on E: Exception do begin

if Assigned(LThread) then

FreeAndNil(LThread);

Server.DoListenException(Self, E);

end;

end;

End;
由上述源码可以看到,TCPServer每次侦听到一个连接,就会新建一个idPeerThread,

而当这个idPeerThread触发OnExecute事件的时候,就会调用IdTCPServer1Execute,

所以indy支持多进程是无疑的,而在IdTCPServer1Execute中一定要考虑同步问题

indy的idTcpServer, 大量的client不正常断开造成的问题,求大家帮忙查原因?
首先定义了如下一个记录和指针
type

TSimpleClient = Record

id: longint; //系统编号

utype: string; //gprs, emp, unknow

Name: string; //手机号,登录操作员名称

IP: string; //IP

Port: integer; //端口

Status: string; //NULL 登录中 操作中

LastTime: integer; //登录时间

UpdateTime: Integer; //更新时间

HardWare: String; //硬件类型

DataBackTime: Integer; //监控时间, 超时则断开

end;
PClient = ^TSimpleClient;

//客户新建链接时记录客户端信息到记录中

procedure TfrmNet.TCPServerConnect(AThread: TIdPeerThread);

var Client: PClient;

begin

Client := new( PClient );
Client.id := GetTickCount + Random(1000);

Client.uType := 'GUEST';

Client.IP := AThread.Connection.Socket.Binding.PeerIP;

Client.Port := AThread.Connection.Socket.Binding.PeerPort;

Client.LastTime := GetTickCount;

Client.UpdateTime := Client.LastTime;

Client.Status := '登录中';

Client.Name := 'GUEST';

Client.HardWare := GPS_NAME_UNKNOW;

Client.DataBackTime := 3600; //监控周期
AThread.Data := Pointer( client ); <<指到 athread的指针中

end;

//客户端断开事件中释放

procedure TfrmNet.TCPServerDisconnect(AThread: TIdPeerThread);

var Client: PClient;

begin

Client := Pointer(AThread.Data);

AThread.Data := nil;

end;
//与客户通讯的处理过程

procedure TfrmNet.TCPServerExecute(AThread: TIdPeerThread);

var c: PClient;

begin

if (AThread.Connection.Connected) and (not Athread.Terminated) then

begin

sStr := AThread.Connection.CurrentReadBuffer;

end;

//其他不会造成任何死循环或者异常的处理代码

end;

//关掉当前用户以前打开的tcp/ip链接

//当用户突然断线时,所打开的tcp/ip链接有可能继续保持不断线

procedure TfrmNet.Clients_CloseGprsBeforeConnect( curId: Integer; sName: String );

var i: integer;

list: TList;

Client: PClient;

begin
List := tcpServer.Threads.LockList;

try
for i := 0 to List.Count -1 do begin

try

Client := Pointer( TIdPeerThread(List.Items[i]).Data );

if Client = nil then continue;

if (Client.Name <> sName) or (Client.id = curId ) or (Client.utype <> 'GPRS') then Continue;
if TIdPeerThread(List.Items[i]).Connection.Connected then

TIdPeerThread(List.Items[i]).Connection.Disconnect;

except

TIdPeerThread(List.Items[i]).Stop;

end;

end;
finally

tcpServer.Threads.UnlockList;

end;

end;

问题是:

大量的终端设备通过TCP/IP连接到服务器,由于设备硬件以及使用的是GPRS网络的原因.

设备经常会像断电那样,重新连接服务器,然而之前的连接没有断开(不知道是不是GPRS的原因)

虽然程序已经做了判断,断开这种异常的连接,

但是服务器程序还是经常会死掉,原因不明.请大家帮助分析分析.

求:使用IdTcpClient和IdTcpServer相互发送数据
======================server============================

unit Unit1;
interface
uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, IdBaseComponent, IdComponent,
IdTCPServer, StdCtrls;
type

TForm1 = class(TForm)

Memo1: TMemo;

Edit1: TEdit;

Button1: TButton;

IdTCPServer1: TIdTCPServer;

procedure IdTCPServer1Connect(AThread: TIdPeerThread);

procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);

procedure Button1Click(Sender: TObject);

procedure IdTCPServer1Execute(AThread: TIdPeerThread);

private

{ Private declarations }

public

{ Public declarations }

end;
type

PSocketThread=^TSocketThread;

TSocketThread=Record

SocketThread:TIdPeerThread;

Next:PSocketThread;

end;
var

Form1: TForm1;

ST_Head,ST_End:PSocketThread;

ST_Count:integer;
implementation
{$R *.dfm}
procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);

var

PST_:PSocketThread;

begin

New(PST_);

PST_^.SocketThread:=AThread;

PST_^.Next:=nil;

if ST_Count=0 then

begin

ST_Head:=PST_;

ST_End:=ST_Head;

end

else

begin

ST_End^.Next:=PST_;

ST_End:=PST_;

end;

ST_Count:=ST_Count+1;

Edit1.Text:=IntToStr(ST_Count);

end;
procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);

var

PST_,PST_0:PSocketThread;

begin

PST_:=ST_Head;

PST_0:=ST_Head;

while PST_<>nil do

begin

if PST_^.SocketThread.ThreadID=AThread.ThreadID then

begin

PST_0^.Next:=PST_^.Next;

Dispose(PST_);

ST_Count:=ST_Count-1;

Edit1.Text:=IntToStr(ST_Count);

end else

begin

PST_0:=PST_;

PST_:=PST_^.Next;

end;

end;

end;
procedure TForm1.Button1Click(Sender: TObject);

var

PST_:PSocketThread;

begin

PST_:=ST_Head;

while PST_<>nil do

begin

PST_^.SocketThread.Connection.WriteLn('To U '+IntToStr(PST_^.SocketThread.ThreadID)+#$A);

PST_:=PST_^.Next;

end;

end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);

begin

Memo1.Lines.Add(AThread.Connection.ReadLn);

end;
end.

================================client====================================

unit Unit1;
interface
uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,

IdTCPClient;
type

TForm1 = class(TForm)

IdTCPClient1: TIdTCPClient;

Button1: TButton;

Memo1: TMemo;

Edit1: TEdit;

Button2: TButton;

Button3: TButton;

procedure Button1Click(Sender: TObject);

procedure IdTCPClient1Connected(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure IdTCPClient1Disconnected(Sender: TObject);

procedure FormClose(Sender: TObject; var Action: TCloseAction);

procedure Button3Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;
var

Form1: TForm1;

td:Dword;

doRead:boolean;
implementation
{$R *.dfm}
procedure ReadThread;

var

s:String;

begin

form1.Memo1.Lines.Add('Begin reading...');

s:=Form1.IdTCPClient1.ReadLn;

while doRead do

begin

s:=Form1.IdTCPClient1.ReadLn;

form1.Memo1.Lines.Add(s);

sleep(100);

end;

end;
procedure TForm1.Button1Click(Sender: TObject);

begin

IdTCPClient1.Connect(3000);

doRead:=true;

CreateThread(nil,0,@ReadThread,nil,0,td);

end;
procedure TForm1.IdTCPClient1Connected(Sender: TObject);

begin

Memo1.Lines.Clear;

Memo1.Lines.Add('connected to server');

end;
procedure TForm1.Button2Click(Sender: TObject);

begin

IdTCPClient1.WriteLn(Edit1.Text);

end;
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);

begin

ExitThread(td);

Memo1.Lines.Add('disConnected from server');

end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

doRead:=false;

IdTCPClient1.Disconnect;

ExitThread(td);

end;
procedure TForm1.Button3Click(Sender: TObject);

begin

// IdTCPClient1.Disconnect;

doRead:=false;

end;
end.

如何解决使用IdTcpServer时CPU利用率很高的问题?

程序主要功能就是使用IdTcpServer将数据发送给每个连接的IdTcpClient我试过两种方法.

但是服务端程序的CPU利用率很高,占用了所有资源.我试过用Application.ProcessMessages

但是无效,我又禁止了所有界面的操作也没用.没发数据的时候也一直居高不下,我用了IdAntiFreeze也没什么效果,还有用了IdThreadMgrPool也
没用,

程序虽然能正常运行,但CPU利用率却这么高,没道理呀,难道是我的程序处理逻辑有问题?还是有什么其他地方没考虑或设置周到?

那位有类似的代码参考一下,或者有没有什么其他更好的方法,功能要求简单:只要将数据从服务端单方向发送给每给客户端就可以了(不考虑用U
DP,不考虑用广播包,因为需要他能在INTERNET上运行)
方法一:SERVER端使用EXECUTE发送,客户端建立一个线程接收

procedure TCastProxy.TCPServerExecute(AThread: TIdPeerThread);

begin

// application.ProcessMessages;

try

athread.Connection.WriteStream(tempclient.ClientData,true,true,0);

tempclient.ClientData.Clear;

except

on e:exception do begin

Athread.Connection.Disconnect;

end;

end;

end;
procedure TCastProxy.TcpClientThreadRun(Sender: TIdCustomThreadComponent);

var

Adata:TmemoryStream;

begin

AData:=TmemoryStream.Create;

// application.ProcessMessages;

if assigned(Adata) then begin

if Tcpclient.Connected then begin

// Adata.Clear;

try

tcpclient.ReadStream(Adata,-1,false);

// .........

except

on e:exception do begin

tcpclient.Disconnect;

end;

end;

end else begin

try

Tcpclient.Connect(1000);

except

on e:exception do

statusbar.SimpleText:=e.Message;

end;

end;

end;

Adata.Free;

end;
方法二:SERVER端使用循环发送给每个客户,客户端使用线程接收

if TcpServer.Active then begin

try

Threads:=Tcpserver.Threads.LockList;

for temp:=0 to Threads.Count-1 do begin

try

adata.Position:=0;

TIdPeerThread(Threads[temp]).Connection.WriteStream

(Adata,true,true,adata.Size);

except

On E:Exception do

TIdPeerThread(Threads[temp]).Connection.Disconnect;

end;

end;

finally

TcpServer.Threads.UnlockList;

end;

end;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: