与Socket的“再次见面”
注:
这是一个针对 网络开发领域初学者 的系列文章,可作为《.NET 4.0 面向对象编程漫谈 》一书的扩充阅读,写作过程中我假设读者可以对照阅读此书的相关章节,不再浪费笔墨重复介绍相关的内容。
对于其他类型的读者,除非您已经有相应的.NET 技术背景与一定的开发经验,否则,阅读中可能会遇到困难。
我希望这系列文章能让读者领略到网络开发的魅力!
另外,这些文章均为本人原创,请读者尊重作者的劳动,我允许大家出于知识共享的目的自由转载这些文章及相关示例,但未经本人许可,请不要用于商业盈利目的。
本文如有错误,敬请回贴指正。
谢谢大家!
金旭亮
=================================================
点击以下链接阅读本系列前面的文章:
《 开篇语—— 无网不胜》
《我在“网” 中央 》
=================================================
前一篇文章介绍了与Socket的第一次“约会”经历,看来双方的第一印象不错,不然读者也不会做出一个“继续交往”的决定,并继续阅读本篇文章了。 :)
在这篇文章中,计划介绍两方面的内容:
Socket的连接请求队列和如何开发“一问一答”的网络应用程序。
1 Socket的“连接请求队列”
在现实生活中,一名品貌俱佳的女孩总是不乏追求者,人们会打趣地说“她的追求者有一个班”,这样的一个女孩,在同一时间段内收到多个约会请求实在是太常见的事,当然,到底接受谁的约会邀请,就要看她中意谁了。
类似地,当一个绑定到特定终结点的Socket对象开始“监听(Listen)”时,完全有可能有多个客户端向此Socket同时发出多个连接请求,Socket对象必须决定如何处理这些连接请求。
为了方便处理,操作系统也会给每个正在监听的Socket分配一个“连接请求队列”,将收到的客户端连接请求追加到此队列中:
图 1
由于网络服务器上可能同时会有多个Socket同时监听,我们应该给每个Socket对象分配一个 “适当容量”的队列,以避免过多地占用系统资源,让网络服务器能服务更多的在线用户。
请求队列的容量由Socket.Listen方法的参数backlog确定:
public void Listen(int backlog );
例如,以下代码指定newSocket对象最多只能接收10个连接请求:
newsock.Listen(10 );
更详细地说,如果newSocket对象正在忙于处理某个连接请求时,操作系统又接收到了多个尝试连接此newSocket对象的请求,操作系统会把这些请求追加到newSocket对象所关联的请求队列中。
newSocket对象处理完当前连接之后,它再从关联的请求队列中取出一个处理。
一个有趣的问题是:如果某一时间段内连接请求很多,请求队列“放不下”怎么办?
请看示例解决方案ConnectionQueueDemo。这个解决方案中的MyServerApp项目与前一讲 中的完全一样,也是接收并显示客户端发来的一句“问好”消息之后就断开与此客户端的连接。
为了直观地体现连接请求队列的特性,MyServerApp项目有意识地指定一个容量为“1”的请求队列:
newsock.Listen(1);
在客户端项目MyClientApp2中,创建10个线程,每个线程都创建一个Socket对象并尝试着连接MyServerApp程序(图 2)。
图 2
我们看到了非常著名的“目标计算机积极拒绝……”的信息,它是由以下这句英文直译过来的:
…… the target machine actively refused it ……
就汉语来说,这明显是一个病句,什么叫“积极拒绝”?难道还有“消极拒绝”?
由此可见负责汉化Windows的程序员要不是“语文”学得不太好,要不就是“过于死板”了,个人觉得,此处意译为以下这句更好:
尝试连接的计算机拒绝了连接请求。
从示例中我们可以得到结论:
当尝试连接的客户端发来的连接请求数目过大,超过了服务端Socket的连接请求队列容量时,未能进入队列的请求将会被丢弃,而客户端将会得到一个代码为10061的SocketException。 客户端可以捕获此异常,等待一定的时间再尝试连接。
2 开发“一问一答”的网络应用程序
在各种网络应用程序中,“一问一答”可以说是最常见的一种类型。基于Socket实现这种类型的网络应用程序是非常简单的:
在服务端与客户端配套使用Send和Receive方法,只要传送的数据不超过缓冲区的大小限制,就可以实现“一问一答”的标准C/S架构程序。
请看示例解决方案EchoServiceDemo(图 3):
图 3
EchoServiceDemo示例没什么复杂的,大家看看示例源码就清楚了。
看懂源码后,请读者做做以下实验:
(1)先运行服务端程序EchoServiceApp,然后,运行客户端程序EchoServiceClientApp连接上服务端,与EchoServiceApp进行交谈,注意不要输入“exit”命令退出。
(2)再运行EchoServiceClientApp的另一个实例,尝试连接EchoServiceApp,发生了什么?
(3)运行EchoServiceClientApp的第3个实例,尝试连接EchoServiceApp,又发生了什么?
你能利用已经掌握的知识解释发生这些现象的原因吗?从这个实验中,您体会到了什么?总结出来了哪些经验?
进一步地,读者现在能解决以下这个问题吗?
你能想办法修改EchoServiceApp源码,让它同时可以服务多个客户端应用程序吗?
提示:
(1)在EchoServiceApp程序中为每个客户端连接创建一个独立的线程,在此线程中完成“一问一答”的工作。
(2)感到困难的读者请参看《.NET 4.0面向对象编程漫谈》,系统地学习第16章《多线程开发技术基础》。
此处只是介绍了开发“一问一答”的网络应用程序的最基础开发技巧,其实,这其中还有许多需要注意的“陷阱”,下一讲将介绍Socket编程中最大的“陷阱”--缓冲区问题,并开发第一个真正有点实用价值的网络应用程序--网络四则运算计算器。
========================================
(在博客园:http://files.cnblogs.com/bitfan/AskAndAnswerSourceCode.rar )
联系客服