Indy自身就很复杂,只做一个最简单复习了。
1.
TIdCustomTCPServer的StartListening启动,建立socket,bind,然后listen,然后建立TIdListenerThread线程,让TIdListenerThread来听。
2.TIdListenerThread的Run里
进来就
LYarn := Server.Scheduler.AcquireYarn;
这得到一TIdYarnOfThread的实例(创建了线程),默认情况线程是TIdSchedulerOfThreadDefault的NewThread建立的,FreeOnTerminate为true,也就是说线程跑完就Free了。
然后select(250) fd,如果有连接到来,就accept,用来存储accept结果的是TIdIOHandlerSocket类。然后开始建立TIdTCPConnection,它接管TIdIOHandlerSocket。建立TIdContext类(是TIdTask的子类),根据TIdTCPConnection创建任务(Task)了,并将一些TIdCustomTCPServer的事件给TIdContext
LContext := Server.FContextClass.Create(LPeer, LYarn, Server.Contexts); LContext.FServer := Server; LContext.OnBeforeRun := Server.ContextConnected; LContext.OnRun := Server.DoExecute; LContext.OnAfterRun := Server.ContextDisconnected; LContext.OnException := Server.DoException;3. 先明确几个类
TIdTask,任务,也就是要做的事情的内容。
TIdYard,这个就是一个载体,啥也不是,可理解为TObject。
TIdThreadWithTask,这个是一个TThread的子类,它有一个TIdTask的属性。
TIdYarnOfThread,是TIdYarn的子类,有两个属性:TIdScheduler和 TIdThreadWithTask。
根据2的步骤,Thread和Task都有,那么就差执行了,于是让Scheduler来执行
Server.Scheduler.StartYarn(LYarn, LContext); 在这里,新的线程就开始执行了。
执行的方式为TIdThreadWithTask的Run里调用Task的Run,而Task的Run,就是Server.DoExecute了。所以TIdTCPServer的好几个事件,是在TIdThreadWithTask线程里面运行的,如果要在这些事件里修改VCL的东西或者释放资源(最好是不要去修改VCL的东西),要特别留意同步问题,别和主线程死锁了(比如发消息用PostMessage而不要SendMessage)
4.至于为啥要让Scheduler来管理,这主要是为了扩展吧,比如线程池,EMBT自己提供的一个类叫TIdSchedulerOfThreadPool,这个类在DataSnap里面用到了。TDSTCPServerTransport的PoolSize和MaxThreads两个属性,就是给TIdSchedulerOfThreadPool用的。PoolSize,表示初始化是会建立PoolSize个TIdThreadWithTask线程放着备用,MaxThreads表示TIdSchedulerOfThread.NewThread能够New出来的最大线程数。
另外,我这里一直说线程线程,其实Indy的TIdYard的设计,主要还是为了不同平台的兼容(TIdYard这载体啥都能放啊),Indy还支持Windows的纤程(Fibers),只要将TIdTCPServer的Schedule设置为TIdSchedulerOfFiber即可。纤程这东西,比较复杂,理论上说比thread少耗资源,也能突破一进程2028个线程的限制,可控制要用户自己切题。再说了,真的要上大连接量的应用,谁也不会用Indy来找死。
最后,Indy的主站www.indyproject.org早不更新了,要下载最新代码请到indy.fulgan.com/ZIP/。据说Indy11正酝酿中,期待ing。
没有评论:
发表评论