您的位置:首页 > 其它

一步步搭建自己的web服务器

2015-04-02 15:04 323 查看

IIS或者其他Web服务器究竟做了哪些工作,让浏览器请求一个URL地址后显示一个漂亮的网页?要想弄清这个疑问,我想我们可以自己写一个简单的web服务器。


思路:

创建socket监听浏览器请求。

连接成功,接受浏览器的请求数据。

响应浏览器的请求。(我们只响应静态文件) 。

好的,思路很清晰。下面就跟着我动手一步步用代码实现。

1、创建socket监听浏览器请求。

当浏览器请求域名,DNS将域名解析为IP地址。带着缺省的端口80访问我们的服务器。所以Socket的监听,也需要绑定IP和端口。

代码:

// 窗体加载
private void Form1_Load(object sender, EventArgs e)
{
// 关闭线程操作检测
Control.CheckForIllegalCrossThreadCalls = false;
Log("服务器启动中 >>>");
IPAddress ip = IPAddress.Parse(txtIP.Text);
IPEndPoint p = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
socketWatch.Bind(p);
socketWatch.Listen(10);
// 为了不阻塞主线程,开启后台线程,监听浏览器请求。
Thread t = new Thread(Listen);
t.IsBackground = true;
t.Start();
Log("服务器启动成功。");
}


Listen 进程只需要无限循环接受浏览器发送过来的请求。

private void Listen()

{

   while (true)

   {

     Socket sender=socketWatch.Accept();

     Log(sender.RemoteEndPoint.ToString() + "连接成功");

}

}

辅助方法,显示提示信息。(不重要)

// 显示提示信息
private void Log(string msg)
{
txtMsg.AppendText(msg + "\r\n");
}


执行结果

打开我们的winform程序。

打开浏览器访问: http://192.168.95.1:3099/ 地址

就能看到以下结果:

class HttpResponse
{
public int StatusCode { get; set; }
public Dictionary<int, string> Dic = new Dictionary<int, string>();
public string ContentType { get; set; }
public int ContentLength { get; set; }
public byte[] Data { get; set; }
public Socket Socket { get; set; }
public Action<string> Log { get; set; }
public HttpResponse(string rawUrl, Socket socket, Action<string> log)
{
this.Socket = socket;
FillDic();
this.Log = log;
// 判断是否为静态资源
if (Judge(rawUrl))
{
// IO 读取资源
Data = ReadFile(rawUrl);
if (Data == null)
{
// 404 没有这个文件
StatusCode = 404;
string msg = "没有找到这个文件。";
ContentType = "text/html";
Data = Encoding.Default.GetBytes(msg);
ContentLength = Data.Length;
ContentType = "text/html";
SendData();
}
else
{
ContentLength = Data.Length;
StatusCode = 200;
ContentType = GetContentType(rawUrl);
// 响应请求
SendData();
}
}
else
{
// 不处理
string msg = "只处理静态文件。";
Data = Encoding.Default.GetBytes(msg);
ContentLength = Data.Length;
StatusCode = 500;
ContentType = "text/html";
// 响应请求
SendData();
}

}

// 判断文件是否为静态资源
private bool Judge(string rawUrl)
{
bool isStatic = false;
string ext = Path.GetExtension(rawUrl);
switch (ext)
{
case ".html":
case ".htm":
case ".jpg":
case ".png":
case ".gif":
case ".js":
case ".css":
case ".ico": isStatic = true; break;
}
return isStatic;
}

// 响应
private void SendData()
{
StringBuilder sb = new System.Text.StringBuilder();
sb.Append("HTTP/1.1 " + StatusCode + " " + Dic[StatusCode] + "\r\n");
sb.Append("Server: ksn/1.0\r\n");
sb.Append("Content-Type: " + ContentType + "\r\n");
sb.Append("Content-Length: " + ContentLength + "\r\n");
sb.Append("\r\n");
Log("响应报文: \r\n" + sb.ToString());

byte[] head = Encoding.UTF8.GetBytes(sb.ToString());
List<byte> res = new List<byte>();
res.AddRange(head);
if (this.Data != null)
{
res.AddRange(this.Data);
}
Socket.Send(res.ToArray());

}

private string GetContentType(string rawUrl)
{
string ct = "text/html";
string ext = Path.GetExtension(rawUrl);
switch (ext)
{
case ".js": ct = "text/javascript";
break;
case ".css": ct = "text/css";
break;
case ".jpg": ct = "image/jpeg";
break;
case ".png": ct = "image/png";
break;
case ".gif": ct = "image/gif";
break;
case ".ico": ct = "image/x-ico";
break;
}
return ct;
}

// 初始化状态码
private void FillDic()
{
Dic.Add(200, "Ok");
Dic.Add(404, "Not Found");
Dic.Add(500, "Internal Error");
}

// 读取请求的资源
private byte[] ReadFile(string rawUrl)
{
string path = AppDomain.CurrentDomain.BaseDirectory;
path = path + "web" + rawUrl;
if (File.Exists(path))
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
return data;
}
}
else
{
return null;
}

}
}


View Code

这个类的代码比较多,但是不难,都是按照顺序一步步往下走的。其实web服务器大致就做了这些事情。只不过封装的东西更多,功能更完善。

资源:

项目源码下载地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: