armfly 发表于 2021-6-10 11:14:38

IDUdpServer的用法


IDUdpServer研究心得


Indy10中的控件IDUdpServer使用方便,比之Indy9有较大的改动,不过使用的时候一定要先弄清楚它的基本工作流程哦,不然会带来很大的麻烦,一下是本人经过查看源代码及N多测试得出的一些心得:(1)    使用多线程把控件的ThreadedEvent设置为true后控件事件就会在绑定线程内执行了,这个多线程给人的感觉好像是每个连接创建一个线程,其实不是这样的。Udp是没有连接概念的,而事实上是每个绑定套接字(Binding:TIdSocketHandle)绑定创建后都会开启一个线程,这个线程专门处理当前绑定套接字的收发工作。(2)    OnUDPRead事件AThread、ABinding直接的关系上面我们也说到了每个绑定套接字(Binding:TIdSocketHandle)绑定创建后都会开启一个线程,这样就是说这两个参数是一一对应,每个AThread的线程工作期间只处理当前的绑定套接字的收发。(3)    Dll内使用IdUdpServer无法执行OnUDPRead事件?答案其实是否定的,如果我们把ThreadedEvent设置为true的话在dll内OnUDPRead事件是可以正常激发的,我们可以看看一下代码:procedure TIdUDPListenerThread.Run;varPeerIP: string;PeerPort : TIdPort;ByteCount: Integer;beginif FBinding.Select(AcceptWait) then try    // Doublecheck to see if we've been stopped    // Depending on timing - may not reach here if it is in ancestor run when thread is stopped    if not Stopped then begin      SetLength(FBuffer, FServer.BufferSize);      ByteCount := GStack.ReceiveFrom(FBinding.Handle, FBuffer, PeerIP, PeerPort, FBinding.IPVersion);      SetLength(FBuffer, ByteCount);      FBinding.SetPeer(PeerIP, PeerPort, FBinding.IPVersion);    if FServer.ThreadedEvent then begin                     //备注(1)      UDPRead;      end else begin      Synchronize(UDPRead);      end;    end;except    // exceptions should be ignored so that other clients can be served in case of a DOS attack    on E : Exception do    begin      FCurrentException := E.Message;      FCurrentExceptionClass := E.ClassType;      if FServer.ThreadedEvent then begin      UDPException;      end else begin      Synchronize(UDPException);      end;    end;end;end;这段代码是套接字工作线程的主要实现代码,从备注(1)加粗处我们可以看到如果IdUdpServer的属性ThreadedEvent为false时会使用到了Synchronize进行主线程同步,而Synchronize方法是无法在dll应用程序内正常使用的,所以大家使用时候要注意这点。
(4)    Server.ReceiveBuffer、Binding.Receive方法使用注意事项请看看一下代码:    if UDPController.Bindings.Readable(20000) then begin      try      SetLength(FBuffer,2048);      RecvLen:=UDPController.Bindings.Receive(FBuffer);      BytesToRaw(FBuffer,_LogonDataPackage,SizeOf(_LogonDataPackage));      //Result := Binding.RecvFrom(ABuffer, VPeerIP, VPeerPort, AIPVersion);       AThread.UDPRead;            //备注1      except      // exceptions should be ignored so that other clients can be served in case of a DOS attack      on E : Exception do      begin            AThread.UDPException;   //备注2          end;      end;      end;    end    else begin      RecvLen:=0;end;注意备注部分,这样是不恰当的,地方AThread.UDPRead时,读的数据却是上次的数据,不是我们预想的结果,故需使用以下代码:    if UDPController.Bindings.Readable(20000) then begin      try      SetLength(FBuffer,2048);       RecvLen:=UDPController.Bindings. RecvFrom(FBuffer,CurrPeerIP,CUrrPeerPort, UDPController.Bindings.IPVersion);// 设置当前Peer,很重要      UDPController.Bindings.SetPeer(CurrPeerIP,CUrrPeerPort,Binding.IPVersion);      BytesToRaw(FBuffer,_LogonDataPackage,SizeOf(_LogonDataPackage));
        AThread.Server.OnUDPRead(AThread,FBuffer,AThread.Binding);      except      // exceptions should be ignored so that other clients can be served in case of a DOS attack      on E : Exception do      begin            AThread.Server.OnUDPException(AThread,AThread.Binding,E.Message,E.ClassType);      end;      end;      end;    end    else begin      RecvLen:=0;    end;(5)    在绑定线程外不宜使用Server.ReceiveBuffer、Binding.Receive等方法接收数据报绑定线程外接收数据报如接收到不是自己所需数据时,很难把此次数据拷挂到绑定线程内激发OnUDPRead,使得OnUDPRead事件只能响应在绑定线程外,这样给程序设计和跟踪都带来了不小的麻烦,因此如在绑定线程外实现接收数据报效果最好建立自己的接收队列,使用轮训接收数据报队列的方式实现。

转载于:https://www.cnblogs.com/zhmore/archive/2010/08/29/1811921.html

wangjun110 发表于 2021-6-30 14:02:02

这是passcall语言吗
页: [1]
查看完整版本: IDUdpServer的用法