惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
博客园_首页
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
美团技术团队
小众软件
小众软件
V
V2EX
博客园 - Franky
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
Attack and Defense Labs
Attack and Defense Labs
S
Security Affairs
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
T
The Exploit Database - CXSecurity.com
有赞技术团队
有赞技术团队
S
Schneier on Security
人人都是产品经理
人人都是产品经理
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
Kaspersky official blog
PCI Perspectives
PCI Perspectives
AI
AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
O
OpenAI News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Register - Security
The Register - Security
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
博客园 - 【当耐特】
C
Cisco Blogs
大猫的无限游戏
大猫的无限游戏
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
S
Securelist
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
L
LangChain Blog
SecWiki News
SecWiki News
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V2EX - 技术
V2EX - 技术
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
L
LINUX DO - 热门话题
Cisco Talos Blog
Cisco Talos Blog

博客园 - 张皓

Stereoscopic Player 1.7.4 (SSP) 加载字幕 layout_weight体验(实现按比例显示) Linux5配置jboss环境 pager-taglib 分页扩展实例 DisplayTag1.2 扩展(自定义分页、排序、导出、页面导航) EJB3在JBoss5内集群探究 maven2 Jetty运行多模块的web application JBoss5开发web service常见问题 jboss启动常见的错误 ADO连接池 DLL内线程同步主线程研究(子线程代码放到主线程执行) DBGrid内使用CheckBox功能 子绑定控件获取父绑定项的值 - 张皓 - 博客园 IdTcpServer 用户掉线检测方法 . Net环境下消息队列(MSMQ)对象的应用[转] 消息队列(Message Queue)简介及其使用[转] 分布式事务TransactionScope小结[转] 分布式事务的点滴 调试SQL Server存储过程方法
IDUdpServer研究心得
张皓 · 2010-08-29 · via 博客园 - 张皓

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;

var

  PeerIP: string;

  PeerPort : TIdPort;

  ByteCount: Integer;

begin

  if 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[0].Readable(20000) then begin

      try

        SetLength(FBuffer,2048);

        RecvLen:=UDPController.Bindings[0].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[0].Readable(20000) then begin

      try

        SetLength(FBuffer,2048);

        RecvLen:=UDPController.Bindings[0]. RecvFrom(FBuffer,CurrPeerIP,CUrrPeerPort, UDPController.Bindings[0].IPVersion);

// 设置当前Peer,很重要

      UDPController.Bindings[0].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事件只能响应在绑定线程外,这样给程序设计和跟踪都带来了不小的麻烦,因此如在绑定线程外实现接收数据报效果最好建立自己的接收队列,使用轮训接收数据报队列的方式实现。