HanDs
管理员

[Visual Studio文章] 用VB写Web服务器 





学习中请遵循国家相关法律法规,黑客不作恶。没有网络安全就没有国家安全

本站需要登陆后才能查看

用VB写Web服务器
(一) 引言

web服务是internet上应用最为广泛的服务之一,每天大量的http连接几乎构成了整个网络世界,大量的应用必然要求优秀的web服务器,目前主要应用的web服务器大概包括:microsoft internet information server和apache.其中,apache是基于unix和linux的服务器,当然现在win环境下也有apache的版本,不过它主要还是应用在unix下。而microsoft internet information server则是microsoft公司提供的基于win-的web服务器,目前,流行的版本主要是4.x和5.x。可是作为一个程序员的您,或者作为一个电脑爱好者的您,当然希望自己亲手写一个web server,一来可以学习,主要是学习http1.1协议.二来可以练习自己对编程的熟悉程度,对于希望提高自己在网络编程方面的功力的读者而言,写一个web服务器绝对是上上之选。本文将选择使用microsoft visual basic配合vb自带的winsock控件来开发一个简单的web服务器,(其实,说简单也不尽然,因为我们的服务器实现了一些特殊的功能)。

(二) 为什么选择vb做开发工具?

我之所以要把这个单独拿出来作为一个部分呢?原因很简单:很多朋友看不起vb,认为那只不过是小孩子的玩的东西罢了!其实这是大错特错的,vb是一个功能强大的面向企业级的开发工具,可以快速的构造基于win-的应用程序,并拥有强大的功能,经过microsoft多年的苦心经营,vb现在已经发展到了很不错的地步,在5.0及其以后的vb中,我们可以直接编译成本机代码而不是p-code代码,这大大增强了vb程序的运行速度,极大的改善了这一原来最大的弱点。针对我们今天的主题,vb提供了winsock控件,microsoft在winsock内封装了win- socket的大部分功能,同时把这一切变的简单,程序员不需要了解什么是socket,什么是阻塞与非阻塞模式,而只需要很简单的设置几个属性,调用几个方法,就可以完成网络程序的编写。使得程序员可以将精力集中到主要的矛盾上,而不是拘泥与语言本身。这也是我为什么选择vb作为讲解语言的原因之一。

(三) 关于http协议

好了!现在我们解决了开发工具的问题,要开始今天的主题了,我们要开始编写我们自己第一个http服务器了!
首先,我们必须对http协议有一个大致的了解。http全称是:hyper text transfer protocol,也就是超文本传输协议。目前使用的版本是1.1版。它定义了几十条命令,但总的来说,一个http连接应该包括以下4个部分:
1) connection 客户端连接服务器。
2) send request 客户端发送一个request也就是一个请求。
3) send response 服务器解析这个request,并回送一个response。
4) disconnection 当所有的response都发送完成后,服务器关闭连接。
以上4个方面中,第一个和第四个很简单,而request是客户端生成的,需要服务器解析的一个字符串,而response则是服务器根据request生成的一个字符串。可见,我们最主要的一个工作就是对request的处理和生成response。
我们先来看一个典型的request。它是internet explorer 5.0生成的 :
get / http/1.1
accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
accept-language: zh-cn
accept-encoding: gzip, deflate
user-agent: mozilla/4.0 (compatible; msie 5.01; win- nt 5.0)
host: localhost
connection: keep-alive

这就是一个request,下面我来逐条解释一下, get / http/1.1这一行中,get是命令,表示要下载文件,而后面跟的符号 \ 则表示需要下载是默认的html文件,在后面的http/1.1则表明使用的http协议的版本,这里使用的是1.1版,其他常用的还有1.0和0.9。下来一行accept表示接受的文件类型,也就是支持的文件类型,我们发现,在这里ie支持gif文件,jpeg文件等等。接下来的accept-language和accept-encoding分别表示接受的语言和编码方式,至于user-agent则指出了客户端使用的软件类型和操作系统。接下来host则指出服务器地址。而connection这个字段没有多大用处,一些服务器也还不支持,这里也就不讨论了。以上就是一个典型的request。需要注意的是,每一行之间要用一个回车加换行来分隔。在vb中,这就是vbcrlf。下面我们继续看一个request:
post /xyl-bin/printinfo.xyl http/1.1
accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
referer: http://localhost/xyl-bin/getinfo.htm
accept-language: zh-cn
accept-encoding: gzip, deflate
user-agent: mozilla/4.0 (compatible; msie 5.01; win- nt 5.0)
host: localhost
content-length: 75
connection: keep-alive

name=xin&age=20&subject=student&other=i+am+a+programmer&submit=%cc%e1%bd%bb
这也是一个reques,它是一个客户端向服务器发送数据的时候常用到,也就是一些表格之类的网页提交后的结果。以上和前面的request相同的我就不多说了,我们把精力主要放在新出现的地方上。首先,第一行的post取代原来的get,post的意思是告诉服务器我有数据要给你.而post后面的/xyl-bin/printinfo.xyl则告诉服务器处理这些数据的文件的位置,很明显,在我们的例子中,它是位于根目录下xyl-bin目录下一个叫printinfo.xyl的文件(这些文件通常都是一些cgi)。在下面几行出现的referer则表明产生这些数据的网页名,这里叫getinfo.htm. content-type则表明了数据的格式以及编码的方式。这点在internet上很重要,我们以后还会遇到.接下来的content-length也就是数据的长度,在这一切都结束的时候,会出现一个空行,然后开始数据。注意它的格式. = 前面的是变量名,后面的则是值.之间用&符号分隔。至于i+am+a+programmer其实就是i am a programmer.这里的+表示一个空格.这些都是经过url编码后的格式,我们的服务器必须对这些做出正确的解释。
说了这么多,大家是否发现一个request的结构是不是很简单的呢?其实这就是一个规则,通过一系列预先定义的规则来实现一定的功能,这点在网络编程中经过会见到。到了这里,很容易猜测response的结构是否和request相同呢?恭喜你,猜中了!我们还是先来看一个response。

http /1.1 200 ok
server:myhtpp server
content-length: 678

(这里开始html正文)

这是一个最简单的response,由于我们是自己来开发的server,不需要太华丽的功能,所以我们的服务器只生成简单的response.我先来解释一下各行的意思. http /1.1 200 ok,这句表示服务器找到了文件而且成功解释了request,这是很常见的一句.其中200是状态码.表示ok,一切正常.其他常用的还有诸如404表示文件没找到.302则是重定向到其他的url.这需要和location字段配合使用。由于我们要开发的是一个用来学习的简单的web服务器,我们不打算完全的支持http1.1,而只打算支持它的一个子集,所以我们只对我们需要使用的字段进行一些简单的介绍。其实response的字段有很多,常用的有last modified,content-typt,content-length等。意思和大约和request中的差不多,很容易举一反三,就不一一详述了。

(三) 开始编码

以上我们大致的学习了http协议的一些基础知识,现在开始我们要进行最重要的工作:编码。将我们的知识用vb代码来表示出来,也就是写一个web服务器。首先,打开visual basic 6.0,新建一个工程,命名为myhttpserver,然后为它增加一个winsock控件,起名sersock,设置它的index属性为0,这样做的原因是我们需要同时对多个客户端的连接作出响应而不能过多的浪费内存(使用多个winsock控件),于是我们选择使用控件数组。然后在它的connectionrequest事件增添如下代码:

nowuser = nowuser + 1
load sersock(nowuser)
sersock(nowuser).localport = 0
sersock(nowuser).accept requestid
form1.text1.text = form1.text1.text & sersock(nowuser).remotehostip & " connected" & vbcrl

其中nowuser是一个全局变量,用来标志用户的数量。这段代码首先将用户的数目加一,然后读入一个winsock控件,并调用它的accept方法来完成它的连接。然后在一个叫text1的文本框内显示一个用户连接的信息。下面来开始编写dataarrival事件的代码,这比较复杂,其中涉及到了对request的分析。先看一个函数,它可以返回给定的request中的文件。
public function getfilename(byval str as string) as string
dim getfile as string
dim p as string
dim i as integer
i = 6
if mid(str, 1, 3) = "get" then
do
p = mid(str, i, 1)
if p = "/" and i = 6 then
getfile = defhtmlpage
exit do
end if
getfile = getfile + p
i = i + 1
loop until p = " "
if getfile = " " then
getfile = defhtmlpage
else
getfilename = "error strng"
end if
getfilename = getfile
end function

这个函数可以处理当请求为get并且没有额外数据的时候的情况,在这样一个典型的应用下,它返回所请求的文件名。
在知道了所请求的文件后,下面必须测试该文件是否存在,如果是,用http编码,如果不是,那就发送404过去。下面的函数可以完成测试一个文件十分存在的功能。

public function testfileexist(byval file as string) as boolean
on error goto notf
n = filelen(file)
testfileexist = true
exit function
notf:
testfileexist = false
end function

注意该函数使用了vb的错误捕捉机制,这个函数的原理是如果对一个不存在的文件使用一个文件函数,那么就会发生一个错误,我们的函数就可以捕捉这个错误并返回一个false,又如果一切正常,就说明这个文件存在,那就返回true。

知道了用户请求的文件是否存在,那下面就要将该文件发送过去,那下面的函数可以完成这个功能。

public function sendhtmlpage(byval htmlpage as string, byval i as integer) as boolean
if testfileexist(htmlpage) = false then
call send404_page(i)
exit function
else
dim htmlhead as string
dim htmltext as string
dim htmld
if testgrafile(htmlpage) = false then
htmltext = readhtmlfile(htmlpage)
else
htmltext = readgrafile(htmlpage)
end if
htmlhead = "http /1.1 200 ok" & vbcrlf & "server:xinhtpp server"
if testgrafile(htmlpage) = false then
htmlhead = htmlhead & vbcrlf & "content-length:" & str(len(htmltext))
else
htmlhead = htmlhead & vbcrlf & "content-length:" & str(filelen(htmlpage))
end if
htmlhead = htmlhead & vbcrlf & vbcrlf
htmld = htmlhead & htmltext
form1.sersock(i).senddata htmld
form1.text1.text = form1.text1.text & vbcrlf & form1.sersock(i).remotehostip & " get page :" & htmlpage & vbcrlf & vbcrlf
if recflag = true then t = recconnect(http_rec, form1.sersock(i).remotehostip, htmlpage)
end if
sendhtmlpage = true
end function

该函数首先检查文件是否存在,不存在发送一个404页面过去。存在的话,对他进行http编码,然后把编码后的数据发送过去。这就完成了一个服务器最主要的方面。而recconnect则是在日志文件中写数据。而readhtmlfile则是把文件读入内存,这很容易实现.过一会我会给出这两个函数的代码.不过现在,读者自己还是先想想应该怎么写.
现在我们可以把dataarrival事件写成下面的形式,当然这只是极为简单的情形,实际情况要复杂的多的多.

dim str as string
sersock(index).getdata str,vbstring
sendhtmlpage(getfilename(str),index)

这就实现了主要的编程,当然为了客户端知道我们已经发送完了数据,那么很有必要在send_complete事件里面写如下代码:

sersock(index).close

这只是极为简单的关闭了连接,如果不关闭连接的话,那么客户端会一直等待下去.就好象网络速度很慢时一样.显然,这不是我们希望看到的.

上面的代码只是完成了一个web服务器应该完成的很大一部分工作中很少的一点点,不过他已经可以很好的工作了,读者如果有兴趣,最好亲手试着测试一下.下面我给出readhtmlfile的代码,方便那些不想自己写代码的读者.

public function readhtmlfile(byval file as string) as string
dim f as integer
dim d as string
f = freefile
open file for input as f
do
if eof(f) then exit do
line input #f, l
d = d & l & vbcrlf
loop
close #f
readhtmlfile = d
end function

上面的函数只是很简单的把文件的内容读入内存,这样处理起来会快很多.
最后我们来看看recconnect

public function recconnect(byval recfile as string, byval conip as string, byval page as string) as integer
on error goto handel
dim f as integer
dim dat as string
dim test as boolean
recfile = app.path + "\" + recfile
f = freefile
if testfileexist(recfile) = false then
open recfile for output as #f
else
open recfile for append as #f
end if
dat = dat & vbcrlf
dat = "time : " & str(time) & vbcrlf
dat = dat + "date : " & str(date) & vbcrlf
dat = dat + conip + " getted page : " + page
print #f, dat
close #f
recconnect = true
exit function
handel:
recconnect = false
end function

这个函数可以在当前目录下生成一个记录文件,文件名由全局变量recfile给出,格式为:
time : (当前日期)
date : (当前时间)
(给出下载文件名和下载的客户端ip)
这样的话,以后如果出现问题,可以通过阅读这个文件来查阅问题在那里.这是一个贴近用户的考虑.
这样一来我们的服务器就初见雏形了!怎么样?是不是很简单?的确,vb是强大的面向的开发工具,我们如此轻松的就完成了别人在c++里要n行代码才可以完成的问题,这就是vb的可爱之处.当然,我们写的这个服务器还只是一个原形,很多大量的工作还没有完成,比如发送二进制的文件,如图片,可执行文件等,甚至还可以加上个小的防火墙什么的,这一切都留给读者您来处理吧!我是累了~~要睡觉去咯..........................


学习中请遵守法律法规,本网站内容均来自于互联网,本网站不负担法律责任
VB
#1楼
发帖时间:2016-7-9   |   查看数:0   |   回复数:0
游客组
快速回复