关于idtcpserver的使用

摘要:
使用idTCPServer,如何在连接客户端时获取客户端的IP地址?如前所述,Indy是一个多线程控件。连接到服务器时,将为每个客户端创建一个线程。只要客户端发送数据,就会激活服务器的OnExecute事件。服务器首先响应客户的Connect事件。连接后,将在服务器上自动建立连接线程。此连接线程需要由服务器维护。indy连接线程的最大数量不会超过600个。如果有600个线程不足以使用,则很难使用indy控件。


 用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; 


哈哈,自己解决了.
怪自己没有好好看例子. 


很久了,不太记得了
好像是在server端的execute里面加了一句sleep(100);这个100可以自己改,大于18就可以。就是说发送数据了要休息一下,不要不停的发,你

试试先,不行就把你的代码发给我帮你看看


 

免责声明:文章转载自《关于idtcpserver的使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Electron webview解决Chrome浏览器主页被hao123、360和2345篡改简单有效方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

windows server2012 nVME和网卡等驱动和不识别RAID10问题

安装2012--不识别M.2nVME,下载官方驱动程序,并将其注入没有多个驱动程序的系统--添加ITSK通用驱动程序:|Win8012R2.x64网卡驱动程序无法打开--提取官方驱动程序EXE文件以添加网卡驱动程序不识别SATARAID10--超过2T,最大Legacy为2T。...

Element UI 弹窗(Dialog)改成自适应高度,仅body内容部分滚动

定义样式如下:.abow_dialog{display:flex;justify-content:center;align-items:Center;overflow:hidden;.el-dialog{margin:0auto!important;height:90%;overflow:hidden;.el-dialog__body{position:ab...

flutter Radio单选框

单选框,允许用户从一组中选择一个选项。...

PowerQuery清理非文件名字符(清除指定列表中的所有字符)

在左侧导航窗格的空白区域右击,依次找到空白查询项接下来的思路是:遍历列表SearchList中的所有项,依次清理Data表中所有想要处理的列。第三个参数是一个函数:它告诉List.Accumulate函数,在每一次使用SearchList中某一项操作Data表时,其操作的方式是如何的。...

Activiti-个人任务

1.分配任务所有者1.1固定分配在业务流程建模期间指定固定任务所有者;在properties视图中,填写Assignee项作为任务所有者;注:通过固定分配方法,任务是逐步执行的,任务负责人将根据bpmn的配置分配给每个任务;1.2表达式分配1.2.1 UEL表达式Activiti使用UEL表达式,UEL是javaEE6...

ES基本查询总结

ES与数据库比较查询操作Elasticsearch中当我们设置Mapping完毕后,就可以按照设定的方式导入数据。以下内容的原文需要参考ES官方文档1、结构化检索针对字段类型:日期、时间、数字类型,以及精确的文本匹配。结构化检索特点:*1)结构化查询,我们得到的结果总是非是即否,要么存于集合之中,要么存在集合之外。term查询是简单的,它接受一个字段名以及我...