C# | socket实现简单的web服务器
2014-05-24 10:51
585 查看
最近在搬砖做web相关的东西。 因为要解除对一些框架的依赖, 所以有机会稍微接触一些底层的东西。
传统的web服务器包括IIS, tomcat, apache,Nginx都是基于http请求与响应来实现数据对接的。
在C#和java中都可以由httpwebRequest或者servlet相关的类或者包实现封装http的一些数据, 这样我们就不需要自己手动创建http格式的数据了。
而本文要透过这些类, 去看http到底包含了哪些东西, 然后封装属于自己的http响应数据,再通过socket发送给客户端(浏览器), 实现web服务器的一些简单的响应功能。
关于http协议的一些具体细节, 推荐这篇blog:HTTP协议详细
关于http我们要了解:
1.在TCP/IP体系结构中,HTTP属于应用层协议,位于TCP/IP协议的顶层。浏览Web时,浏览器通过HTTP协议与Web服务器交换信息。这些信息(文档)类型的格式由MIME定义。
2.HTTP按客户/服务器模式工作,HTTP是无状态的.
3.HTTP使用元信息作为头标。这里的元信息就是指那些用来指明http具体要做什么的数据,包括请求行或者报头等等。
4.HTTP的请求
方 法 说 明
GET 请求读取一个Web页面
HEAD 请求读取一个Web页面的头标
PUT 请求存储一个Web页面
POST 附加到命名资源中
DELETE 删除Web页面
LINK 连接两个已有资源
UNLINK 取消两个资源之间的已有连接
5.Http常见的请求数据格式:
(1)请求数据:(最常用的两种请求是get跟post)
通常包括:请求行 + 请求报头 + 请求正文
请求行:动作(get等), URL(请求文件的位置), HTTP协议的版本号
请求报头:一些请求的具体信息。(这个可以自己看到的, 抓包)
请求正文: 注意请求正文与请求报头之间必须要有一个空行来区分。
(2)响应数据:
通常:状态行 + 响应报头 + 响应正文
状态行:HTTP协议版本号 + 状态码 + 状态描述
这里常见状态码有:
200 (OK): 找到了该资源,并且一切正常。
304 (NOT MODIFIED): 该资源在上次请求之后没有任何修改。这通常用于浏览器的缓存机制。
401 (UNAUTHORIZED):<strong></strong>客户端无权访问该资源。这通常会使得浏览器要求用户输入用户名和密码,以登录到服务器。
403 (FORBIDDEN):<strong></strong>客户端未能获得授权。这通常是在401之后输入了不正确的用户名或密码。
404 (NOT FOUND):<strong></strong>在指定的位置不存在所申请的资源。
响应报头: 响应的一些格式。通常包含传输数据的格式,数据长度等等。
响应正文:要返回给客户端的数据, 这里也注意与响应报头留有空行。
有了这些格式, 我们就可以通过C#构建自己的WEB服务器了。
需要用到socket用来传输数据, 当接受到浏览器端传来的数据时, 我们解析这个http协议, 获取一些需要知道的信息, 比如是什么类型的请求, 需要请求服务器上什么物理位置的文件等。 然后我们按照http的标准协议封装响应数据的三个部分, 发送给浏览器。
如果想要详细深入理解Web的请求与响应的流程, 推荐:浏览器与服务器数据交换细节, 写的相当好。
这里存在一些小的Bug:应该是请求数据时候的缓冲区大小出现了问题, 等待解决。
传统的web服务器包括IIS, tomcat, apache,Nginx都是基于http请求与响应来实现数据对接的。
在C#和java中都可以由httpwebRequest或者servlet相关的类或者包实现封装http的一些数据, 这样我们就不需要自己手动创建http格式的数据了。
而本文要透过这些类, 去看http到底包含了哪些东西, 然后封装属于自己的http响应数据,再通过socket发送给客户端(浏览器), 实现web服务器的一些简单的响应功能。
关于http协议的一些具体细节, 推荐这篇blog:HTTP协议详细
关于http我们要了解:
1.在TCP/IP体系结构中,HTTP属于应用层协议,位于TCP/IP协议的顶层。浏览Web时,浏览器通过HTTP协议与Web服务器交换信息。这些信息(文档)类型的格式由MIME定义。
2.HTTP按客户/服务器模式工作,HTTP是无状态的.
3.HTTP使用元信息作为头标。这里的元信息就是指那些用来指明http具体要做什么的数据,包括请求行或者报头等等。
4.HTTP的请求
方 法 说 明
GET 请求读取一个Web页面
HEAD 请求读取一个Web页面的头标
PUT 请求存储一个Web页面
POST 附加到命名资源中
DELETE 删除Web页面
LINK 连接两个已有资源
UNLINK 取消两个资源之间的已有连接
5.Http常见的请求数据格式:
(1)请求数据:(最常用的两种请求是get跟post)
通常包括:请求行 + 请求报头 + 请求正文
请求行:动作(get等), URL(请求文件的位置), HTTP协议的版本号
请求报头:一些请求的具体信息。(这个可以自己看到的, 抓包)
请求正文: 注意请求正文与请求报头之间必须要有一个空行来区分。
(2)响应数据:
通常:状态行 + 响应报头 + 响应正文
状态行:HTTP协议版本号 + 状态码 + 状态描述
这里常见状态码有:
200 (OK): 找到了该资源,并且一切正常。
304 (NOT MODIFIED): 该资源在上次请求之后没有任何修改。这通常用于浏览器的缓存机制。
401 (UNAUTHORIZED):<strong></strong>客户端无权访问该资源。这通常会使得浏览器要求用户输入用户名和密码,以登录到服务器。
403 (FORBIDDEN):<strong></strong>客户端未能获得授权。这通常是在401之后输入了不正确的用户名或密码。
404 (NOT FOUND):<strong></strong>在指定的位置不存在所申请的资源。
响应报头: 响应的一些格式。通常包含传输数据的格式,数据长度等等。
响应正文:要返回给客户端的数据, 这里也注意与响应报头留有空行。
有了这些格式, 我们就可以通过C#构建自己的WEB服务器了。
需要用到socket用来传输数据, 当接受到浏览器端传来的数据时, 我们解析这个http协议, 获取一些需要知道的信息, 比如是什么类型的请求, 需要请求服务器上什么物理位置的文件等。 然后我们按照http的标准协议封装响应数据的三个部分, 发送给浏览器。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Sockets; using System.Net; using System.Threading; using System.IO; namespace WebServer { class MyWebServer { private readonly string webServerRoot = @"E:\Pages\"; private readonly int port = 4096; private readonly string host = "127.0.0.1"; private Socket socket = null; public MyWebServer() { try { IPAddress ip = IPAddress.Parse(host); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(ip, port)); socket.Listen(10); Thread th = new Thread(new ThreadStart(Listen)); th.Start(); Console.WriteLine("Server Is Listening ..."); } catch(Exception e) { Console.WriteLine("Fail When Listening : " + e.ToString()); } } public void SendHttpHead(string httpVersion, string statusCode, string mime, int codeLength, ref Socket socket) { string sendBuffer = ""; sendBuffer += httpVersion + statusCode + "\r\n"; sendBuffer += "Query Ok In May GMT"; if (mime.Length == 0) { mime = "text/html"; } sendBuffer += "Content-Type: " + mime + "\r\n"; sendBuffer += "Accept-Ranges: bytes\r\n"; sendBuffer += "Content-Length: " + codeLength + "\r\n\r\n"; Byte[] sendDataByte = Encoding.ASCII.GetBytes(sendBuffer); SendToBrowser(sendDataByte, ref socket); // SendToBrowser(sendBuffer, socket); } public void SendToBrowserByString(string sendData, ref Socket socket) { SendToBrowser(Encoding.ASCII.GetBytes(sendData), ref socket); } public void SendToBrowser(Byte[] sendData, ref Socket socket) { try { if(socket.Connected) { int dataNumber = socket.Send(sendData); Console.WriteLine("Send Bytes {0}", dataNumber); } else { Console.WriteLine("Fail in Connecting ..."); } } catch(Exception e) { Console.WriteLine("Error !"); } } public void Listen() { string httpVersion, statusCode, mime; int dataLength; while(true) { Socket mySocket = socket.Accept(); if(mySocket.Connected) { Byte[] dataFromBrow = new Byte[100]; mySocket.Receive(dataFromBrow, dataFromBrow.Length, 0); string receiveBuf = Encoding.ASCII.GetString(dataFromBrow); Console.WriteLine(receiveBuf); if(receiveBuf.Substring(0, 3) != "GET") { Console.WriteLine("sorry, we just can accept the \"get\" request"); mySocket.Close(); return; } int startPos = receiveBuf.IndexOf("HTTP", 1); httpVersion = receiveBuf.Substring(startPos, 8);//get HttpVersion string subString = receiveBuf.Substring(0, startPos - 1); startPos = subString.LastIndexOf("/") + 1; string requestFile = subString.Substring(startPos);//get the FileName string localDir = webServerRoot; if(requestFile.Length == 0) { requestFile = "Index.html"; } string requestFilePath = localDir + requestFile;///get the definite path of the request file if(File.Exists(requestFilePath) == false) { string errorMessage = "404 Error! File Does Not Exists"; SendHttpHead(httpVersion," 404 Not Found", "", errorMessage.Length, ref mySocket); SendToBrowserByString("Error", ref mySocket); // Console.WriteLine("Error : 404"); } else { dataLength = 0; string responseData = ""; FileStream fs = new FileStream(requestFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); BinaryReader reader = new BinaryReader(fs); byte[] bytes = new byte[fs.Length]; int read; while ((read = reader.Read(bytes, 0, bytes.Length)) != 0) { responseData = responseData + Encoding.ASCII.GetString(bytes, 0, read); dataLength += read; } reader.Close(); fs.Close(); mime = "text/html"; statusCode = " 200 OK"; SendHttpHead(httpVersion, mime, statusCode, dataLength, ref mySocket);//200 OK SendToBrowserByString(responseData, ref mySocket); } mySocket.Close(); } } } } class Program { static void Main(string[] args) { MyWebServer myWebServer = new MyWebServer(); Console.ReadKey(); } } }
如果想要详细深入理解Web的请求与响应的流程, 推荐:浏览器与服务器数据交换细节, 写的相当好。
这里存在一些小的Bug:应该是请求数据时候的缓冲区大小出现了问题, 等待解决。
相关文章推荐
- C#中使用Socket实现简单Web服务器
- C#中使用Socket实现简单Web服务器
- C#中使用Socket实现简单Web服务器
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 【转】【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 基于Socket编程实现一个简单的Web服务器
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- C#实现简单WEB服务器
- TCP/IP协议学习(七) 基于C# Socket的Web服务器---动态通讯实现
- 【Web后端笔记】基于Socket实现的简单Web服务器搭建
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- C#实现简单WEB服务器
- ServerSocket实现 一个简单的Web服务器 [引]
- c#实现简单的web服务器
- C#使用Socket实现服务器与多个客户端通信(简单的聊天系统)
- socket实现简单web服务器,可查看http请求信息
- C# Socket实现Http WEB服务器
- C#实现简单WEB服务器
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- C#实现简单WEB服务器