C:\MyPersonalWebServer\C:\MyPersonalWebServer\Data\Mimes.dat - 副檔名對應 MIME 類型VDirs.dat - 虛擬目錄對應實體路徑Default.dat - 預設文件名清單# Mimes.dat
.html;text/html
.htm;text/html
.jpg;image/jpeg
.png;image/png
.css;text/css
.js;application/javascript
# VDirs.dat
/;C:\MyWebServerRoot
/images/;C:\MyWebServerRoot\Images
# Default.dat
index.html
default.html
index.htm
default.htm
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Imtiaz
{
public class MyWebServer
{
private TcpListener listener;
private int port = 5050;
private string webRoot = @"C:\MyWebServerRoot\";
public MyWebServer()
{
try
{
listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Web Server Running on port {0}... Press Ctrl+C to Stop", port);
Thread t = new Thread(StartListen);
t.Start();
}
catch (Exception ex)
{
Console.WriteLine("啟動失敗: " + ex.Message);
}
}
// 取得預設文件名
private string GetDefaultFile(string localDir)
{
try
{
using (StreamReader sr = new StreamReader("Data\\Default.dat"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string full = Path.Combine(localDir, line.Trim());
if (File.Exists(full)) return line.Trim();
}
}
}
catch { }
return "";
}
// 解析虛擬目錄對應實體路徑
private string GetLocalPath(string virtualDir)
{
try
{
using (StreamReader sr = new StreamReader("Data\\VDirs.dat"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
line = line.Trim();
if (line.Length == 0) continue;
int idx = line.IndexOf(';');
if (idx <= 0) continue;
string vdir = line.Substring(0, idx).ToLower();
string real = line.Substring(idx + 1);
if (vdir == virtualDir.ToLower()) return real;
}
}
}
catch { }
return "";
}
// 依副檔名取得 MIME 類型
private string GetMimeType(string fileName)
{
string ext = Path.GetExtension(fileName).ToLower();
try
{
using (StreamReader sr = new StreamReader("Data\\Mimes.dat"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
line = line.Trim();
if (line.Length == 0) continue;
int idx = line.IndexOf(';');
if (idx <= 0) continue;
if (line.Substring(0, idx).ToLower() == ext)
return line.Substring(idx + 1);
}
}
}
catch { }
return "text/html"; // 預設
}
// 發送 HTTP 標頭
private void SendHeader(Socket socket, string httpVer, string mime, int contentLen, string status)
{
string header = $"{httpVer} {status}\r\n" +
"Server: MyCSharpWebServer/1.0\r\n" +
$"Content-Type: {mime}\r\n" +
"Accept-Ranges: bytes\r\n" +
$"Content-Length: {contentLen}\r\n\r\n";
byte[] data = Encoding.ASCII.GetBytes(header);
socket.Send(data);
}
// 發送資料
private void SendData(Socket socket, byte[] data)
{
try { if (socket.Connected) socket.Send(data); }
catch { }
}
// 主監聽迴圈
private void StartListen()
{
while (true)
{
Socket client = listener.AcceptSocket();
if (!client.Connected) continue;
Console.WriteLine("Client connected: {0}", client.RemoteEndPoint);
byte[] buffer = new byte[1024];
int received = client.Receive(buffer);
string request = Encoding.ASCII.GetString(buffer, 0, received);
// 僅支援 GET
if (!request.StartsWith("GET "))
{
client.Close();
continue;
}
// 解析請求行: GET /path/file.html HTTP/1.1
int httpIdx = request.IndexOf("HTTP", StringComparison.OrdinalIgnoreCase);
string httpVer = request.Substring(httpIdx, 8); // "HTTP/1.1"
string requestLine = request.Substring(0, httpIdx - 1).Trim(); // "GET /path/file.html"
string url = requestLine.Substring(4).Trim(); // "/path/file.html"
url = url.Replace('\\', '/');
// 目錄結尾補 / 以利尋找預設文件
if (!url.Contains(".") && !url.EndsWith("/")) url += "/";
// 分離虛擬目錄與檔名
int lastSlash = url.LastIndexOf('/');
string virtualDir = url.Substring(0, lastSlash + 1);
string fileName = url.Substring(lastSlash + 1);
// 解析實體目錄
string localDir = (virtualDir == "/") ? webRoot : GetLocalPath(virtualDir);
if (string.IsNullOrEmpty(localDir))
{
SendError(client, httpVer, "404 Not Found", "Directory not found");
client.Close();
continue;
}
// 若無檔名,尋找預設文件
if (string.IsNullOrEmpty(fileName))
{
fileName = GetDefaultFile(localDir);
if (string.IsNullOrEmpty(fileName))
{
SendError(client, httpVer, "404 Not Found", "No default file");
client.Close();
continue;
}
}
string physicalPath = Path.Combine(localDir, fileName);
string mime = GetMimeType(fileName);
if (!File.Exists(physicalPath))
{
SendError(client, httpVer, "404 Not Found", "File not found: " + fileName);
client.Close();
continue;
}
// 讀取檔案並回應
try
{
byte[] fileBytes = File.ReadAllBytes(physicalPath);
SendHeader(client, httpVer, mime, fileBytes.Length, "200 OK");
SendData(client, fileBytes);
}
catch (Exception ex)
{
SendError(client, httpVer, "500 Internal Server Error", ex.Message);
}
finally
{
client.Close();
}
}
}
private void SendError(Socket socket, string httpVer, string status, string msg)
{
string html = $"{status}
{msg}
";
byte[] data = Encoding.ASCII.GetBytes(html);
SendHeader(socket, httpVer, "text/html", data.Length, status);
SendData(socket, data);
}
}
}
class Program
{
static void Main()
{
new MyWebServer();
Console.ReadLine(); // 保持主執行緒存活
}
}
csc /out:MyWebServer.exe MyWebServer.cs
MyWebServer.exe
瀏覽器輸入 https://localhost:5050/ 或 https://localhost:5050/test.html。
ThreadPool 或 async/await 非同步 Socket。