日期:2011-07-23  浏览次数:20500 次

摘要

这 篇文章讨论了如何使用C#开发一个简单的web服务器应用程序。尽管我们可以使用任何一种支持.NET的编程语言开发,但我选择了C#。本篇文章中的代码 是使用微软的β2版的Visual C# Compiler Version 7.00.9254 [CLR version v1.0.2914]编译通过的,对代码作一些小的改动后,使用β1版也可能编译通过。该web服务器应用程序能够与IIS或其他任何web服务器软件同 时在一台服务器上运行,只要为它指定一个空闲的端口即可。在本篇文章中,我还假定读者对.NET、C#或Visual Basic .Net有一定的了解。

该web服务器应用程序能够向浏览器返回HTML格式的文件,而且支持图像,它不加载嵌入式图像或支持任何一种脚本语言。为了简单起见,我将它开发成一个命令行应用程序。

准备工作

首先,我们需要为这个web服务器应用程序定义一个根文件夹,例如,C:\MyPersonalwebServer,然后在该要根目录下创建一个数据目录,例如,C:\MyPersonalwebServer\Data;最后在数据目录下创建三个文件,例如:

Mimes.Dat
  Vdirs.Dat
  Default.Dat

Mime.Dat中将包含该web服务器支持的MIME类型,其格式为<扩展名>; ,例如:

.html;text/html
  .htm;text/html
  .bmp;image/bmp

VDirs.Dat中包含有虚拟目录的信息,格式为; <物理目录>,例如:

/; C:\myWebServerRoot/
  test/; C:\myWebServerRoot\Imtiaz\

Default.Dat中包含有虚拟目录中文件的信息,例如:

default.html
  default.htm
  Index.html
  Index.htm

为简单起见,我们将使用文本文件存储所有的信息,但我们也可以使用XML等其他的格式。在开始研究代码之前,我们先来看一下在登录网站时浏览器需要传递的头部信息。

我们以请求test.html为例进行说明。在浏览器的地址栏输入http://localhost:5050/test.html(记住,需要在URL中包括端口号),服务器将得到下面的信息:

〈/DRIVE:\PHYSICALDIR〉
  GET /test.html 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: en-usAccept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0; .NET CLR 1.0.2914)
Host: localhost:5050Connection: Keep-Alive
  开始编程
  namespace Imtiaz
  {
  using System;
  using System.IO;
  using System.Net;
  using System.Net.Sockets;
  using System.Text;
  using System.Threading ;
  class MyWebServer
  {
  private TcpListener myListener ;
  private int port = 5050 ; // 可以任意选择空闲的端口
  //生成TcpListener的构建器开始监听给定的端口,它还启动调用StartListen()方法的一个线程
  public MyWebServer()
  {
  try
  {
  //开始监听给定的端口
  myListener = new TcpListener(port) ;
  myListener.Start();
  Console.WriteLine("Web Server Running... Press ^C to Stop...");
  //启动调用StartListen方法的线程
  Thread th = new Thread(new ThreadStart(StartListen));
  th.Start() ;
  }
  catch(Exception e)
  {
  Console.WriteLine("An Exception Occurred while Listening :" +e.ToString());
  }
  }

我们定义了名字空间,包括应用程序必需的引用,初始化了构建器中的端口,启动了端口监听进程,创建了一个新的线程调用startlisten函数。

我们假设用户没有在URL中提供文件名,在这种情况下我们必须自己确定缺省的文件名,并将它返回给浏览器,就象在IIS中的文档标签中定义缺省的文档那样。

我们已经在default.dat中存储了缺省的文件名,并将文件存储在了数据目录中。GetTheDefaultFileName函数将目录路径作为输入参数,打开default.dat文件,在目录中查找文件,根据是否找到了文件返回文件名或一个空格。

public string GetTheDefaultFileName(string sLocalDirectory)
  {
  StreamReader sr;
  String sLine = "";
  try
  {
  //打开default.dat,获得缺省清单
  sr = new StreamReader("data\\Default.Dat");
  while ((sLine = sr.ReadLine()) != null)
  {
  //在web服务器的根目录下查找缺少文件
  if (File.Exists( sLocalDirectory + sLine) == true)
  break;
  }
  }
  catch(Exception e)
  {
  Console.WriteLine("An Exception Occurred : " + e.ToString());
  }
  if (File.Exists( sLocalDirectory + sLine) == true)
  return sLine;
  else
  return "";
  }

象在IIS中那样,我们必须将虚拟目录解析为物理目录。在Vdir.Dat中,我们已经存储了实际的物理目录和虚拟目录之间的映像关系。需要记住的是,在任何情况下,文件的格式都是重要的。

public string GetLocalPa