DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: melqui
今日帖子: 11
在线用户: 24
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 magiewang (magiewang) ▲▲▲△△ -
普通会员
2021/7/29 9:21:19
标题:
求教网络通讯阻塞和非阻塞的运行原理。 浏览:1355
加入我的收藏
楼主: 最近接触到网络通讯组件,阻塞模式以INDY为代表,
非阻塞异步的就非常多了,DIOCP/ICS/CrossSocket等。

其实对这个一直似懂非懂,没能彻底搞清楚,所以特
来请教各位老师,想把基础打结实一些。

阻塞我理解的是一个指令没结束会一直等待,直到结束后
才进行下面操作,流程比较好控制,多线程下很好控制理
解,适合单次传输数据量大的场合,比如文件传输那些。

异步我理解的是非阻塞,指令发出后就返回了,然后通过
事件回调来通知结果,这种模式下按道理不会阻塞界面,
但看了ICS的FTPDEMP,实际上在文件传输过程中,界面一
直是无响应的。那么针对文件传输来说,是否我理解的阻
塞方式会比异步方式更好呢?

在一个服务器端的前提下,要同时接收大量文件传输,应
该用哪种模式比较好?

不知道理解是否正确,请教下各位老师,到底什么网络场
合适合用异步来做?

请不吝赐教!!!!
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2021/7/29 12:35:24
1楼: indy这种应该会有属性或参数来设置阻塞非阻塞模式的。
楼主自己也说到了,阻塞就是等待所有操作完成然后进行下一步,就是最简单的顺序执行的模式。而非阻塞就是不等待完毕立即返回,等操作完成以后通过回调或者消息通知的形式通知调用方操作完成。
其实从代码方面来说,越简单的东西越好,而阻塞就是最简单的顺序执行结构;非阻塞则是我们教科书上基本看不到的并行结构。
复杂程度越高,那么开发成本就越高,我们必须花费大量的时间来排查代码里各种显示或隐式的错误;尤其是那种非特定的错误花费的时间非常高,而在并行结构中这种非特定的、隐藏的错误会很常见。
不过阻塞方式虽然可以大幅度降低开发的复杂程度,但是就像楼主说的,对用户体验来说却非常的不好,最常见的就是在调用一个耗时的功能的时候会因为执行时间过长导致用户UI死锁造成程序假死的效果。
所以你要根据自己实际的应用场景去选择较优方案,而不是像小学生做习题一样死板的去套公式。
----------------------------------------------
--
作者:
男 hnxxcxg (咏南中间件) ★☆☆☆☆ -
盒子活跃会员
2021/7/31 7:50:29
2楼: 楼主的问题不是靠一个贴子就讨论的清楚的。建议先百度弄清楚阻塞/非阻塞的概念。
----------------------------------------------
中间件QQ群: 92449782 博客: http://www.cnblogs.com/hnxxcxg/
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/8/1 21:04:44
3楼: 楼主的问题,其实 Indy 里面有一篇文档解释过,说明了为啥 Indy 要使用阻塞模式。

这个问题要讲历史。

很古老的年代,UNIX 里面,网络通讯都是阻塞模式,使用 UNIX 的人,就是命令行,一行一行往下执行,这个也是程序的基本方式。一行一行往下执行,也就是有上下文的概念。这个【上下文】,在 Indy9 里面,有个参数叫做 Context。

那个年代,WINDOWS 3.0 / 3.1 没有多线程,网络通讯又慢,一个网络通讯,如果阻塞,整个程序停在那等待,界面就冻结了,程序像是死机了一样。因此,微软搞了个异步模式,把网络通讯丢给操作系统在后面跑,应用层的代码继续往下执行。操作系统收到数据或者发送数据成功,再通知应用程序。类似的实现方法,就好似 Delphi 里面的事件,别的地方的代码做完了,用事件通知你的程序。

因为 WINDOWS 在早期只能使用异步的方式写网络程序,搞得很多从 WINDOWS 程序入门的程序员,以为异步方式的网络通讯代码才是正宗写法。

单就网络通讯来说,同步和异步(阻塞/非阻塞),最简单直接的区别,就是你的代码的写法。

阻塞的代码,一行一行往下执行,上下文的关系很清楚。代码逻辑也很清晰,代码简单易懂。

非阻塞的代码,你的代码的逻辑需要分散在几个不同的地方。代码不容易写好,一不小心容易写成面条代码,绕来绕去,绕成一团。代码也不易读。

对于有界面的程序来说,比如 Delphi 写一个桌面(包括手机 APP)程序,如果网络通讯慢,会阻塞界面导致界面冻结,建议网络通讯的代码,不要使用主线程来调用。现代的 WINDOWS,多线程很好用,何必去纠结同步异步。当然,你界面上点了一个按钮,然后另外一个线程去执行网络通讯,执行完成后可能你要通知界面,让界面进行相应的显示,也需要一个事件回调,也有点面条。但现在的 Delphi 加了一些语法糖,可以让你的代码不用分散在几个不同的函数里面,可以一行一行直接往下写。

简单说一下这些语法糖的用法,类似以下写法:

当用户输入一个字符串,点了按钮,网络发送字符串给对方,对方收到字符串以后,思考了10秒,回送了一个字符串 -- 典型的聊天场景:

如果是同步的方法:
OnClick 函数
begin
  网络发送字符串('Hi, 吃饭了吗?');
  
  Memo1.Lines.Add(接收来自网络的字符串);
end;
  
上述写法,有个问题:
1. 假设网络很慢,【网络发送字符串】这个函数执行完要5秒;
2. 对方看到字符串后,思考了5 秒;
3. 对方发送后,网络很慢,接收需要5秒。2和3,导致了【接收来自网络的字符串】这个函数执行了 10 秒。
4. 整个 OnClick 这个事件方法,执行了 15 秒。结果是界面冻结了 15 秒。

如果用 Delphi 带的语法糖:

OnClice
var
  S: string;
begin
  S := Edit1.Text;
  TTask.Run(
  begin
    网络发送字符串(S);
    S := 网络接收字符串;

    TThread.Sync(
    begin
      Memo1.Lines.Add(S)
    end
    );
  end
);  
end;

上述代码,从阅读代码的角度来说,符合正常的代码顺序逻辑。从执行的角度来看,耗时长的两行代码,放进 TTask.Run 里面去跑,其实就是丢给另外一个线程去跑,并没用阻塞主线程,因此界面不会冻结。当网络接收完成后,需要显示到 Memo1 里面时,调用了 TThread.Sync 方法,又将收到的信息同步给主线程了。


----------
当然,楼主问的是,在服务器的前提下。

在服务器的前提下,你不用考虑用户操作,不用考虑冻结主线程的问题。并且,服务器是要并发相应多个客户端的请求的。

在早期的 UNIX / Linux 里面,服务器响应并发请求,是采用多进程的模式。给每个请求分配一个进程。多个不同的进程显然是并发运行的,且互相不干扰。还有个好处是某个进程崩溃了,并不影响其它的进程,不会一个进程崩溃了,其它的客户端也受影响。
但是,多进程模式,对系统资源消耗大。

现在我们用 DELPHI 写服务器端程序,同样可以用多进程模式,比如写一个 WEB 服务器程序,编译成 CGI 的方式在 IIS 底下运行。

用 DELPHI 写服务器端的多线程的程序,一个程序响应多个客户端请求,你如果不想让客户端排队等待,那就用多线程模式。

不管多进程,还是多线程,总之一个客户端的请求在一个逻辑清晰逐行执行的代码里面,是更好的代码写法,你又不担心冻结服务器的界面 --  反正服务器每界面。

我自己的实际经验就是,使用 Indy 写一个 TCP SERVER 是非常简单的事情。你完全不需要去考虑多个用户并发访问的时候会怎样。你只要写好一条连接该如何处理,顺序执行你的代码就好了。这样就避免了面条代码。
----------------------------------------------
-
作者:
男 magiewang (magiewang) ▲▲▲△△ -
普通会员
2021/8/2 10:53:23
4楼: 谢谢 pcplayer,非常专业,感谢。
我之所以提出这个问题,是因为之前一直用Indy和SynapseTCP,这次要写个文件传输的,想到了参考FTP,于是看网上总结ICS的非常稳定,所以就测试了学习了一下ICS的FTP组件,因为ICS是异步方式,在我理解不应该导致界面无响应,但在传输文件过程中,主界面是没有响应的。所以才由此疑问,请教各位老师。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/8/2 11:01:48
5楼: FTP 的话,我用 Indy 做过服务器和客户端,都没什么问题。

服务器端的话,Indy 的阻塞式编程模式非常好。写好的业务逻辑代码是在每个连接对应的线程里面执行的,不用去考虑多线程,它帮你封装好了。因为是多线程,也不用去考虑多个连接会不会互相阻塞的问题,也不用去考虑服务器的界面被冻结的问题。使用 Indy TCP Server 那个控件做的 FTP 服务器,可以是一个标准的有界面的 VCL 程序。

客户端的话,Indy FTP Client 也是阻塞式的。因此,如果想要界面不被冻结,你把它放到线程里面跑就好了。

因为是阻塞式,代码的逻辑顺序很好。因此代码更容易读,也更好管理。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行82.03125毫秒 RSS