?? 使用c#開發自己的web服務器 .txt
字號:
使用C#開發自己的web服務器
摘要
這篇文章討論了如何使用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 GetLocalPath(string sMyWebServerRoot, string sDirName)
{
treamReader sr;
String sLine = "";
String sVirtualDir = "";
String sRealDir = "";
intiStartPos = 0;
//刪除多余的空格
sDirName.Trim();
// 轉換成小寫
sMyWebServerRoot = sMyWebServerRoot.ToLower();
// 轉換成小寫
sDirName = sDirName.ToLower();
try
{
//打開Vdirs.dat文件,獲得虛擬目錄
sr = new StreamReader("data\\VDirs.Dat");
while ((sLine = sr.ReadLine()) != null)
{
//刪除多余的空格
sLine.Trim();
if (sLine.Length > 0)
{
//找到分割符
iStartPos = sLine.IndexOf(";");
// 轉換成小寫
sLine = sLine.ToLower();
sVirtualDir = sLine.Substring(0,iStartPos);
sRealDir = sLine.Substring(iStartPos + 1);
if (sVirtualDir == sDirName)
{
break;
}
}
}
}
catch(Exception e)
{
Console.WriteLine("An Exception Occurred : " + e.ToString());
}
if (sVirtualDir == sDirName)
return sRealDir;
else
return "";
}
我們還必須使用用戶提供的文件擴展名確定Mime類型。
public string GetMimeType(string sRequestedFile)
{
StreamReader sr;
String sLine = "";
String sMimeType = "";
String sFileExt = "";
String sMimeExt = "";
// 轉換成小寫
sRequestedFile = sRequestedFile.ToLower();
int iStartPos = sRequestedFile.IndexOf(".");
sFileExt = sRequestedFile.Substring(iStartPos);
try
{
//打開Vdirs.dat文件,獲得虛擬目錄
sr = new StreamReader("data\\Mime.Dat");
while ((sLine = sr.ReadLine()) != null)
{
sLine.Trim();
if (sLine.Length > 0)
{
//找到分割符
iStartPos = sLine.IndexOf(";");
// 轉換成小寫
sLine = sLine.ToLower();
sMimeExt = sLine.Substring(0,iStartPos);
sMimeType = sLine.Substring(iStartPos + 1);
if (sMimeExt == sFileExt)
break;
}
}
}
catch (Exception e)
{
Console.WriteLine("An Exception Occurred : " + e.ToString());
}
if (sMimeExt == sFileExt)
return sMimeType;
else
return "";
}
下面我們來編寫建立和向瀏覽器(客戶端)發送頭部信息的函數。
public void SendHeader( string sHttpVersion,
string sMIMEHeader,
int iTotBytes,
string sStatusCode,
ref Socket mySocket)
{
String sBuffer = "";
//如果用戶沒有提供Mime類型,則將其缺省地設置為text/html
if (sMIMEHeader.Length == 0 )
{
sMIMEHeader = "text/html"; // Default Mime Type is text/html
}
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: cx1193719-b\r\n";
sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -