导航:
论坛 -> DELPHI技术
斑竹:liumazi,sephil
作者:
2021/1/6 17:30:29
标题:
有没有支持阻塞式的Telnet控件?
浏览:1759
加入我的收藏
楼主:
有没有支持阻塞式的Telnet控件?Indy 的是异步,不知道怎么用同步方式接收数据。
----------------------------------------------
-
作者:
2021/1/10 17:19:42
2楼:
Indy TIdTelnet
----------------------------------------------
-
作者:
2021/1/11 13:36:02
3楼:
@2楼,TIdTelnet 是异步的,从OnDataAvailable 事件接收数据, 我希望是在 SendString ... 之后,用函数或方法自己主动去读取数据,如果还没返回数据,就一直等待直到超时。
----------------------------------------------
-
作者:
2021/1/11 16:00:37
4楼:
可以参考一下IdTelnet.Pas写自己版本的TidTelnet.这个文件并不大,总共才7百多行,其实就是TIdTCPClientCustom + 数据接收线程。 也可以继承现有的TidTelnet,写一个新的SendString函数, 等到全部的数据都收到后,再返回,
----------------------------------------------
-
作者:
2021/1/11 16:14:06
5楼:
如果 TIdTelnet 是异步的,好办。封装一下。就是加一个 TEvent.WaitFor 去阻塞,在 OnDataAvailable 的那个地方去 SetEvent 解除阻塞。 记得要加线程。否则在一个线程里面自己阻塞自己,接触阻塞的代码没线程去执行是不行的。当然如果那个 OnDataAvailable 本身是被控件内部的线程驱动的,那就不需要额外增加线程了。
----------------------------------------------
-
作者:
2021/1/11 17:26:51
6楼:
看了一下它的属性,有个:ThreadEvent,可以勾选。 应该是勾选后,这个事件的代码是被它内部的线程驱动的。 因此,如果你调用它的 SendString 函数是被主线程调用的,比如是用户点了按钮以后被调用的,你就可以拿一个 TEvent 去阻塞它,然后让 OnDataAvailable 里面的代码去接触阻塞。 这样子,很简单就能完成异步变同步。 ---------- 有时候,我们又想把同步变异步,咋办?以前挺麻烦的,要创建一个线程类。现在只需要加上 TTask.Run 就搞定。
----------------------------------------------
-
作者:
2021/1/11 18:20:04
7楼:
你所说的同步,究竟是指线程同步,还是应答同步?两者不是一回事。
----------------------------------------------
-
作者:
2021/1/11 21:59:52
8楼:
楼主既然说阻塞式,那他说的同步,应该是【阻塞/非阻塞】的【同步/异步】。
----------------------------------------------
-
作者:
2021/1/12 8:38:33
9楼:
那很明显,SetEvent只实现了线程同步,还没有阻塞式同步,也就是没由实现第一个命令的应答收到后才能发第二个命令。
----------------------------------------------
-
作者:
2021/1/12 16:12:13
10楼:
SetEvent 实现了阻塞式同步啊。 外面调用 SendString 的函数,被 Event.Waitfor 阻塞了,一直要等到有数据返回才能继续往下走。那么,如果要发第二条,不就是等到第一条命令有应答了才能执行么? 比如你的代码: SendString(MyCommand); SendString(MySecondCommand); SendString(MyThirdCommand); 如果这个函数内部加上 Event.Waitfor 那么就是三条函数逐个执行的。
----------------------------------------------
-
作者:
2021/1/12 17:53:54
11楼:
我看过了idTelnet.pas源代码, 里面的ThreadEvent是普通object event,如OnDataAvalable一类的东西, 不是同步用的TEvent或TSimpleEvent. procedure TIdTelnetReadThread.Run; begin // if we have data run it through the negotiation routine. If we aren't // connected to a telnet server then the data just passes through the // negotiate routine unchanged. // RLebeau 3/29/04 - made Negotiate() get called by Synchronize() to // ensure that the OnTelnetCommand event handler is synchronized when // ThreadedEvent is false if FClient.IOHandler.InputBufferIsEmpty then begin FClient.IOHandler.CheckForDataOnSource(IdTimeoutInfinite); end; if not FClient.IOHandler.InputBufferIsEmpty then begin if FClient.ThreadedEvent then begin FClient.Negotiate; end else begin Synchronize(FClient.Negotiate); end; end; FClient.IOHandler.CheckForDisconnect; end;
----------------------------------------------
-
作者:
2021/1/12 18:05:05
12楼:
ThreadedEvent为True的情况下,接收数据线程直接执行Negotiate,没有与主线程同步。 当然,在你自己程序里面增加一个TEvent是可以实现应答同步的,需要继承TidTelnet写一个新的类,写一个新SendString就可以了
----------------------------------------------
-
作者:
2021/1/12 19:13:07
13楼:
这个 TIdTelnet 的 ThreadEvent 属性,正如我前面说,它勾选或者不勾选,决定的是 OnDataAvalible 事件里面写的代码,是被主线程调用还是被线程调用。 假设不勾选,则事件代码被主线程调用,然后,假设你的 SendString() 方法里面的代码有个 TEvent.Waitfor 等待 OnDataAvalible 事件方法里面的 TEvent.SetEvent,而你的 SendString 方法又是被主线程调用的,那就永远等待了,卡死。死锁。因为主线程已经被 WaitFor 卡住了,停下来了,则 OnDataAvalible 事件里面的 SetEvent 代码永远不会被执行了。 ---------- 强调一下: 代码都是那些代码。但多线程底下,一定要搞清楚你写的那些代码,是在被哪个线程执行。如果有前面说的那种关系,你就要把那两部分代码,分开给两个不同的线程去执行。这种东西搞清楚了其实很简单,不清楚的时候,死得莫名其妙。
----------------------------------------------
-
作者:
dalas (dalas)
★☆☆☆☆
-
普通会员
2021/1/12 20:31:49
14楼:
TIdTelnet 的 ThreadedEvent 只是是否在主线程中执行 Negotiate, 没有提供类似 ReadBuffer 或 ReadString 的函数。 所以只能自己改写 TIdTelnet。 或用土办法,用一个全局变量 FBuf function ReadBuffer(MiSec:Integer=3000):TIdBytes; var aTime1,aTime2:TSystemTime; iTime:Integer; begin Result:=nil; GetLocalTime(aTime1); repeat if FBuf<>nil then begin Result:=FBuf; FBuf:=nil; Break; end; GetLocalTime(aTime2); Application.ProcessMessages; Sleep(1); iTime:=(aTime2.wHour*60*60 + aTime2.wMinute*60 + aTime2.wSecond) * 1000 + aTime2.wMilliseconds -(aTime1.wHour*60*60 + aTime1.wMinute*60 + aTime1.wSecond) * 1000 + aTime1.wMilliseconds; until iTime>=MiSec; end; OnDataAvailable事件里把读取到的数据赋值给FBuf procedure IdTelnetDataAvailable(Sender: TIdTelnet; const Buffer: TIdBytes); begin FBuf:=FBuf+Buffer; end; 用法: SendString('fdsfsdfs'); ReadBuffer(); //默认超时3秒钟,当然并不是非常精确,可能会误差一点,但几乎可以忽略
----------------------------------------------
-
作者:
2021/1/13 15:10:14
15楼:
14楼的意思是达到了,但是不完美,SendString是非阻塞,发送字符串后,指不定OnDataAvailable什么时候触发, 而且由于SendString实际是一个循环一次发送一个字符,理论上每发送一个字符,就触发一次OnDataAvailable, 所以SendString后,ReadBuffer不一定能读取到完整的OnDataAvailable返回数据。 就算用个循环多读几次ReadBuffer,什么时候跳出循环也是个问题。
----------------------------------------------
-
作者:
dalas (dalas)
★☆☆☆☆
-
普通会员
2021/1/13 19:14:13
16楼:
拿 TIdTelnet 改了一下,不是很完美,如果服务端没有返回数据,ReadBytes 会一直阻塞卡死,水平太菜,不会加超时,看有没有高手完善一下,哈哈 增加 Event 属性, 默认 True,跟原版 TIdTelnet 一样。 设置为 False,用 ReadBytes 或 ReadString 读取数据,不触发 OnDataAvailable 事件。
----------------------------------------------
-
作者:
2021/1/14 16:27:53
17楼:
16 楼,其实无需去改 TIdTelnet。按照面向对象的方法,你应该继承它,或者封装它。 至于超时,应该是 TEvent.Waitfor(你的超时值);
----------------------------------------------
-
作者:
2021/1/14 18:01:08
18楼:
楼主,俺特意为你写了一篇博客,有代码: https://blog.csdn.net/pcplayer/article/details/112618625
----------------------------------------------
-
作者:
2021/1/14 20:03:09
19楼:
我用TIdTelnet写的Telnet客户端,有完整代码,没有使用TEvent,供参考: https://blog.csdn.net/BlueStorm/article/details/112631991
----------------------------------------------
-
作者:
2021/1/18 17:03:02
20楼:
谢谢楼上几位,是个不错的解决办法
----------------------------------------------
-
作者:
2021/1/18 17:30:08
21楼:
pcplayer (pcplayer) 的办法有点问题,就是sendstring发送一次后,DataAvailable实际会触发多次,只返回第一次触发的数据,无法返回完整数据。
----------------------------------------------
-
作者:
2021/1/18 23:16:19
22楼:
21 楼,那是你的问题。不是我的问题。 如果你的 Telnet 服务器要收到完整命令,才会返回完整命令,则只会触发一次。 如果你的 Telnet 服务器收到一个命令但会一个字符一个字符的返回,要多次触发该事件,那么,你要在该事件里面自己组合字符串,直到组合出来的字符串是合法的,才解除阻塞。这些是你的业务逻辑决定的。
----------------------------------------------
-