有位小伙伴和我说,什么时候研究一下 VBRichClient
的 WebServer
。
这不最近抽了点时间研究了一下,结果入坑发现这玩意真不太好搞。
话说 VBRichClient
是个啥,其实它是一套用 VB6
开发的 VB6
框架组件程序,用于快速高效地开发 VB
程序。
是的,你没看错,它同样是用 VB6
写的,并不是有些人理所当然认为的用 C/C++
之类的编程语言写的。
使用 VBRichClient
来实现 Web
服务,可能作者原本打造这款 VB
框架神器的重点并不在于此,因而其功能实现非常简单。
是真的简单,从代码上就能看出来,它仅仅能提供基本的 Web
访问服务,完全无法与其他同类产品比较。
不过我猜想这个功能我们可以将其运用到自己研发的程序中,比较某应用程序提供临时性的 Web
访问服务等等。
好了,更多的介绍大家可以到网上查一查资料,现在废话不多说,接下来让我们一起去看看这个 VBRichClient
的 WebServer
到底怎么玩,到底能玩到什么程度!
VBRichClient
中的 cWebServer
类
要想让 VBRichClient
实现 Web
服务功能,我们就必须要用到 cWebServer
类。
它有一个属性、一个方法和三个事件,像这样。
是不是挺简单的?
别忙,其实还要用到另外两位好兄弟。
一位叫 cWebRequest
,另一位叫 cWebResponse
,他俩都是 VBRichClient
的类成员,和 cWebServer
类一样,与 cWebServer
关系密切。
cWebRequest
,即请求,是指 cWebServer
接收客户端发来的请求,然后处理之。
cWebResponse
,即响应,是指 cWebServer
接收客户端请求后返回给客户端的处理结果。
如果你对 HTTP
的访问请求和响应完全不了解,那么我还是建议你先学习一下。
因为这对于是否能写好代码很重要,因为这是原理,不懂原理不但写不好代码,而且会有很多天坑等着你。
不过请放心,我是个好人,我不是来吓唬你的,我只是来告诉你哪里有坑,避免你踩上或少踩。
好了,即使你不太了解 HTTP
里的知识(其实我也懂不了多少),其实我也是可以尝试着帮你有个大概的了解,怎么学还不是学,对吧!
你看哈,通常我们打开一个网站,肯定是先输入一个网址回车,然后服务端有了响应后回送给我们相应的网站内容,对吧?
其实这就是一个非常常见典型的 HTTP
建立连接并传输数据的过程。
但是这里有两个需要我们理解,同时也比较容易误解的地方。
一,HTTP
是建立在 TCP
协议上传输的(HTTP 3.0
不算),为了避免加重系统负担,一般情况下页面加载完毕后或经过一定时间这个连接就自动断开了。
想要再访问就再连接一次,因此它的动作就先从客户端到服务端,然后服务端再回客户端,中间可能建立了不止一个连接,但从传输方向来看是一去一回。
二,请求和响应的关系是成对的。
有请求才可以有响应,服务端不会无缘无故发送响应给客户端,这和 C/S
模式的网络程序不太一样。
因此这里就要注意了,我们在程序中要找到请求处理的地方,然后再根据请求内容返回预想的响应结果。
具体什么意思呢?
比如 cWebRequest
中有一个 Response
属性,是的,请求类里面居然有一个响应属性。
其实这就是指服务端在收到请求后,用这个 Response
属性来返回响应,而不是服务端自说自划自行发个响应给客户端。
啰嗦了一大堆,不知道小伙伴们能看懂不?
总结就简单一句话:不管连接怎么建立、建立几次,请求响应(数据传输)就是成对出现一去一回。
总之有一些 HTTP
和 TCP
知识基础的话理解起来就很容易了。
接下来我们一起来看看实际使用起来是什么效果。
WebServer
的代码啥样子
网上大量充斥着摘抄雷同的代码,基本都一样,我将它改了几处如下。
Option Explicit
Private WithEvents WS As RC6.cWebServer
Private Sub Command1_Click()
Set WS = New cWebServer
WS.Listen "WebRootDirectory", "127.0.0.1", 8888
End Sub
Private Sub WS_ConnAccepted(hSocket As Long, ClientIPAndPort As String)
Debug.Print “客户端请求:” & hSocket
Debug.Print "IP连接信息:" & ClientIPAndPort
End Sub
Private Sub WS_ProcessRequest(Request As vbRichClient5.cWebRequest)
Request.Response.SetResponseDataString "hello, world!!<br>你好!网管小贾!"
End Sub
简单地说明一下吧!
方法 Listen
指开启 Web
服务侦听,它有三个参数,分别是 站点根目录
、 IP地址
以及 端口
。
事件 ConnAccepted
指客户端有请求过来并与服务端连接成功后获取的一些客户端信息。
事件 ProcessRequest
指客户端与服务端连接成功后需要处理请求并响应的部分。
网上除了上述代码外我再也找不到更新鲜的了,于是我自己动手添加了更多的代码,最后制作的应用程序有兴趣的小伙伴可以到文末下载。
(为防止白嫖党出没,我会另开帖子将源代码以付费形式放出,请大家理解!)
先给各位秀一下效果哈!
在成功显示的页面中,我们按下 F12
调出浏览器的编辑调试模式。
找到发送请求的消息头信息,我们可以看到 ServerName
是 RichClient-WebServer
。
也就是说,我们的确是向 VBRichClient
的 WebServer
发送了请求并得到了它的响应。
我都做了些啥,这个程序又是怎么玩的呢?
下面就向各位小伙伴汇报演出!
程序基本功能的演示
在正式启动 Web
服务之前,我们先确认服务端口(比如我这是 8888
,默认是 80
)上并没有任何其他服务在跑。
很简单,在命令提示符中输上一条命令。
netstat -anp tcp | findstr -i 8888
返回结果空空如也,的确不曾有什么程序在占用端口 8888
。
OK,确认过后我们就可以放心大胆地开启 Web
服务了。
注意你的根目录,如果不是程序当前目录别忘了改一下哈!
默认页(图片误写为文件名)一般不用改,就是 index.html
。
好了,点击 Start
开启服务吧!
从图中可以看到, Web
服务已经成功开启,服务正欢快地跑在 8888
端口上。
当然现在是没有任何连接的,只是服务在侦听等待请求连接。
好,我们尝试连接一下看看。
在浏览器的地址栏上输入以下网址并回车。
http://127.0.0.1:8888/
嘿,有反应了,我们可以看到有两个客户端请求连接过来了。
并且可以通过命令查看到有两个 TCP
连接已经被建立起来。
只是,呃...为啥浏览器上无法显示页面呢?
其实是由于服务端没有正确返回我们想要的结果。
什么意思呢?
简单地说,是由于我们并没有告诉服务端当接收到什么请求后应该指定哪些内容来发送。
怪我罗?那我们怎么指定呢?
非常简单,我们就拿 index.html
这种常见的文档来做测试吧!
你可以指定默认页为 index.html
,也可以直接访问 index.html
,而事实上我们的根目录下并不存在这个 index.html
文件,所以也就没有正确返回内容给我们了!
在 Web
根目录下,我们准备好一个文本文件,名字就叫 index.html
。
其中内容我们写得简单一些,比如像下面这样的 HTML
测试代码。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>网管小贾 / sysadm.cc</title>
</head>
<body>
Hello, World!!
<br>
你好!网管小贾!!
</body>
</html>
OK,我们接着测试,在浏览器地址栏上指定访问 index.html
,输入以下网址并回车。
http://127.0.0.1:8888/index.html
乃斯!网页内容完美呈现!
这里插句话哈,对于指定了默认页是 index.html
的情况下呢,其实网址就不用特意加上它也能访问。
此外我们从查看 TCP
的结果中也可以了解到,每次访问实际上都要建立一个或多个连接。
为什么总是要提到这个 TCP
连接呢,因为 HTTP
就是在 TCP
上跑的呀(HTTP 3.0
当我没说)!
所以说,学习一下 TCP
也是非常有利于搞懂 HTTP
的,建议小伙伴们注意了解一下这个知识点。
每次访问网站 HTTP
都会建立连接,这个我们都懂没问题,但是好像网页加载完或数据传输完后,连接就会在一定的时间之后自动断开。
具体的情况我才疏学浅不甚了解,但的确除此之外当你主动关闭浏览器窗口时,原来的连接就会断开。
那我就突发奇想,为了节约系统资源,能不能在一定条件(比如我不关闭浏览器)下主动断开连接呢?
经过我的研究,VBRichClient
中 WebServer
的事件 ConnRemoved
似乎存在 Bug
,它老提示我过程声明不匹配,明明我就是照抄它的名字不会错呀!
我发了一封邮件给 VBRichClient
的作者,不知道会不会石沉大海。
在得到回复之前,我尝试看能不能通过构造网址访问来断开连接。
比如,我发送带有 reset
字样的网址。
http://127.0.0.1:8888/reset
显然服务端在接收到请求后肯定是会分析网址的,通过比对网址字段我就可以在代码中加上断开连接的功能了,就像其他网站提交注销用户那样。
主要是通过匹配列表框中的连接历史记录来断开并删除现有连接。
不过往往同一访问连接却并不止一个,有并发的情况存在,因此目前这个删除连接的功能做得还不够完善,代码仅供参考。
其实最好的办法还是利用 ConnRemoved
事件自动触发,奈何这个事件不好使,有待以后继续研究吧!
可以下载文件吗?
下载文件和访问 html
文件似乎是差不多的,都是请求 HTTP
连接嘛,只不过在返回请求的时候要代码要换一换,换成返回文件的方法。
SetResponseFileNameAbs
因此事情就变成挺简单了,只要我们判断客户端的请求,如果是访问某些特殊文件的,比如 xlsx
或是 pdf
等后缀的文档,那么我们就认为浏览器想下载这些文件,继而使用前面的那个方法即可。
接下来就不要响应 html
页面元素了,而是返回文件即可。
比如下载一些新鲜有趣的 xlsx
文件。
亦或是一些喜闻乐见的 pdf
文档。
只是让我感到奇怪的是,xlsx
文件在下载时后缀名为何变成了 zip
?
虽说 xlsx
其实就是个 zip
文件,但是下载下来还要手动修改一下多少有点麻烦哈!
不知道是我哪儿没搞好呢,还是 VBRichClient
的 Bug
,以后有空再研究研究。
写在最后
在众多 Web
引擎大行其道的当下,VBRichClient
的 WebServer
很显然不是拿出来和它的前辈比较的。
以我个人的看法,应该是原作者在写网络模块代码的时候顺手给攒了这么一个小朋友级别的 WebServer
。
有可能就是这个原因,导致了它不仅太过简单,而且其中的 ConnRemoved
事件好像也并不灵光,不知道是不是作者房贷压力大还是怎么,似乎有点还未完成的意思在里边。
不过瑕不掩瑜,对于我们这些小白们也无所谓,把它拿来学习一下编程知识倒还算很OK了,简单易学。
可能有的小伙伴会说,你还测试并发呢!
好吧,目前看像是个玩具级别的程序,去测试它的并发好像并没有什么意义哈,就当作业留给大家吧!
VBRichClient4WebServer.7z(5.12M)
下载链接:https://pan.baidu.com/s/1JscxQ8HPa0BVv8SXE7dIMQ
提取码:
★扫码关注公众号, 发送【000981】获取阅读密码
有兴趣的小伙伴可以先将我做的这个小程序下载下来体验一下。
如果你在运行程序时弹出了如下图的报错警告,那么很有可能是你的 VBRichClient
版本有点旧,尽量用新一些的吧!
较旧版本的库可能对于某些功能还没有完善,对了,话说会不会是由于这个原因(作者还在开发中)导致 ConnRemoved
事件没法用呢?
最后我想说的是,如果大家谁有知道如何触发 ConnRemoved
事件的话,恳请不吝赐教!
好了,今天就写这些吧,别忘记关注转发分享哦!
将技术融入生活,打造有趣之故事。
网管小贾 / sysadm.cc