zoukankan      html  css  js  c++  java
  • 关于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; 


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


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

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


     

  • 相关阅读:
    [Leetcode 56] 55 Jump Game
    [Leetcode 57] 61 Rotate List
    [Leetcode 61] 73 Set Matrix Zeros
    [Leetcode 62] 74 Search a 2D Matrix
    [Leetcode 64] 78 Subsets
    [Leetcode 63] 77 Combinations
    [Leetcode 58] 63 Unique Path II
    python学习笔记第1章节 基础知识
    python学习笔记第2章节 分支,循环,还有条件
    visual studio 2008 试用版评估期已结束的解决方法(转载)
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940840.html
Copyright © 2011-2022 走看看