DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jeff1314
今日帖子: 9
在线用户: 1
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 xxda123 (xxda123) ▲▲△△△ -
普通会员
2021/1/6 17:30:29
标题:
有没有支持阻塞式的Telnet控件? 浏览:1759
加入我的收藏
楼主: 有没有支持阻塞式的Telnet控件?Indy 的是异步,不知道怎么用同步方式接收数据。
----------------------------------------------
-
作者:
男 codecoolie (CodeCoolie) ★☆☆☆☆ -
普通会员
2021/1/8 23:43:22
1楼: 20年前我用过 TNCNX
----------------------------------------------
FFmpeg for Delphi http://www.CCAVC.com http://www.DelphiFFmpeg.com
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/10 17:19:42
2楼: Indy TIdTelnet
----------------------------------------------
-
作者:
男 xxda123 (xxda123) ▲▲△△△ -
普通会员
2021/1/11 13:36:02
3楼: @2楼,TIdTelnet 是异步的,从OnDataAvailable 事件接收数据,
我希望是在 SendString ... 之后,用函数或方法自己主动去读取数据,如果还没返回数据,就一直等待直到超时。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/1/11 16:00:37
4楼: 可以参考一下IdTelnet.Pas写自己版本的TidTelnet.这个文件并不大,总共才7百多行,其实就是TIdTCPClientCustom + 数据接收线程。
也可以继承现有的TidTelnet,写一个新的SendString函数, 等到全部的数据都收到后,再返回,
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/11 16:14:06
5楼: 如果 TIdTelnet 是异步的,好办。封装一下。就是加一个 TEvent.WaitFor 去阻塞,在 OnDataAvailable 的那个地方去 SetEvent 解除阻塞。

记得要加线程。否则在一个线程里面自己阻塞自己,接触阻塞的代码没线程去执行是不行的。当然如果那个 OnDataAvailable 本身是被控件内部的线程驱动的,那就不需要额外增加线程了。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/11 17:26:51
6楼: 看了一下它的属性,有个:ThreadEvent,可以勾选。

应该是勾选后,这个事件的代码是被它内部的线程驱动的。

因此,如果你调用它的 SendString 函数是被主线程调用的,比如是用户点了按钮以后被调用的,你就可以拿一个 TEvent 去阻塞它,然后让 OnDataAvailable 里面的代码去接触阻塞。

这样子,很简单就能完成异步变同步。

----------

有时候,我们又想把同步变异步,咋办?以前挺麻烦的,要创建一个线程类。现在只需要加上 TTask.Run 就搞定。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/1/11 18:20:04
7楼: 你所说的同步,究竟是指线程同步,还是应答同步?两者不是一回事。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/11 21:59:52
8楼: 楼主既然说阻塞式,那他说的同步,应该是【阻塞/非阻塞】的【同步/异步】。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/1/12 8:38:33
9楼: 那很明显,SetEvent只实现了线程同步,还没有阻塞式同步,也就是没由实现第一个命令的应答收到后才能发第二个命令。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/12 16:12:13
10楼: SetEvent 实现了阻塞式同步啊。

外面调用 SendString 的函数,被 Event.Waitfor 阻塞了,一直要等到有数据返回才能继续往下走。那么,如果要发第二条,不就是等到第一条命令有应答了才能执行么?

比如你的代码:

SendString(MyCommand);
SendString(MySecondCommand);
SendString(MyThirdCommand);

如果这个函数内部加上 Event.Waitfor  那么就是三条函数逐个执行的。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
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;
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/1/12 18:05:05
12楼: ThreadedEvent为True的情况下,接收数据线程直接执行Negotiate,没有与主线程同步。
当然,在你自己程序里面增加一个TEvent是可以实现应答同步的,需要继承TidTelnet写一个新的类,写一个新SendString就可以了
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
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秒钟,当然并不是非常精确,可能会误差一点,但几乎可以忽略
----------------------------------------------
-
作者:
男 xxda123 (xxda123) ▲▲△△△ -
普通会员
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 事件。
此帖子包含附件:dalas_2021113191412.rar 大小:806.0K
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/14 16:27:53
17楼: 16 楼,其实无需去改 TIdTelnet。按照面向对象的方法,你应该继承它,或者封装它。

至于超时,应该是 TEvent.Waitfor(你的超时值);
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/14 18:01:08
18楼: 楼主,俺特意为你写了一篇博客,有代码:

https://blog.csdn.net/pcplayer/article/details/112618625
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/1/14 20:03:09
19楼: 我用TIdTelnet写的Telnet客户端,有完整代码,没有使用TEvent,供参考:
https://blog.csdn.net/BlueStorm/article/details/112631991
此帖子包含附件:bluestorm8_2021114202654.rar 大小:53.6K
----------------------------------------------
-
作者:
男 xxda123 (xxda123) ▲▲△△△ -
普通会员
2021/1/18 17:03:02
20楼: 谢谢楼上几位,是个不错的解决办法
----------------------------------------------
-
作者:
男 xxda123 (xxda123) ▲▲△△△ -
普通会员
2021/1/18 17:30:08
21楼: pcplayer (pcplayer) 的办法有点问题,就是sendstring发送一次后,DataAvailable实际会触发多次,只返回第一次触发的数据,无法返回完整数据。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/1/18 23:16:19
22楼: 21 楼,那是你的问题。不是我的问题。

如果你的 Telnet 服务器要收到完整命令,才会返回完整命令,则只会触发一次。

如果你的 Telnet 服务器收到一个命令但会一个字符一个字符的返回,要多次触发该事件,那么,你要在该事件里面自己组合字符串,直到组合出来的字符串是合法的,才解除阻塞。这些是你的业务逻辑决定的。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行693.3594毫秒 RSS