当输入:127.0.0.1:5050 GET / HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Accept-Language: zh-CN User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko Accept-Encoding: gzip, deflate Host: 127.0.0.1:5050 DNT: 1 Connection: Keep-Alive Cookie: s_pers=%20s_fid%3D5E4387B367FCC613-0DC7FD9C6D76C087%7C1453856077838%3B%20s_vs%3D1%7C1390785877860%3B%20s_nr%3D1390784077867-New%7C1422320077867%3B http://127.0.0.1:5050/index.html GET /index.html HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Accept-Language: zh-CN User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko Accept-Encoding: gzip, deflate Host: 127.0.0.1:5050 DNT: 1 Connection: Keep-Alive Cookie: s_pers=%20s_fid%3D5E4387B367FCC613-0DC7FD9C6D76C087%7C1453856077838%3B%20s_vs%3D1%7C1390785877860%3B%20s_nr%3D1390784077867-New%7C1422320077867%3B 提交表单 POST / HTTP/1.1 Host: 127.0.0.1:5050 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1 Cookie: s_pers=%20s_fid%3D5E4387B367FCC613-0DC7FD9C6D76C087%7C1453856077838%3B%20s_vs%3D1%7C1390785877860%3B%20s_nr%3D1390784077867-New%7C1422320077867%3B Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 51 txtName=&txtClass=&txtPassword=&Submit=%CC%E1%BD%BB 提交表单 2 POST /CCC.asp HTTP/1.1 Host: 127.0.0.1:5050 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1 Cookie: s_pers=%20s_fid%3D5E4387B367FCC613-0DC7FD9C6D76C087%7C1453856077838%3B%20s_vs%3D1%7C1390785877860%3B%20s_nr%3D1390784077867-New%7C1422320077867%3B Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 68 txtName=name&txtClass=class&txtPassword=password&Submit=%CC%E1%BD%BB 提交表单3 POST /CCC.asp HTTP/1.1 Host: 127.0.0.1:5050 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1 Cookie: s_pers=%20s_fid%3D5E4387B367FCC613-0DC7FD9C6D76C087%7C1453856077838%3B%20s_vs%3D1%7C1390785877860%3B%20s_nr%3D1390784077867-New%7C1422320077867%3B Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 93 txtName=%D5%C5%C8%FD&txtClass=%B8%DF%D2%BB%B6%FE%B0%E0&txtPassword=990749&Submit=%CC%E1%BD%BB
以上是 浏览器在浏览时发送给服务器的数据内容.在不同情况下会有所不同.不过,一般数据量都很小.
以上的 %D5%C5%C8%FD 实际上是 urlencode 编码方式:在程序开发的过程,需要对其进行解码
由于IIS的不稳定性和难实施性(对操作系统太挑剔),一直想做个自己的轻量级Web服务器。
今天终于抽出时间,借助网络上的实例,实现了自己的服务器,计划以后在项目开发时尝试取代IIS服务器。
在此把整体实现过程记录下,以备后用。
技术实现原理:
1.需要了解HTTP的工作原理
2.需要了解基于C#的SOCKET编程,TcpListener类对象,监听端口
一、启动VS2005,新建一个控制台程序,命名:ConsoleApplication1
二、在添加CS文件,使名:MyWebServer.cs
三、实现类MyWebServer,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace WindowsFormsApplication1 { class MyWebServer { private TcpListener myListener; private int port = 8899; // 选者任何闲置端口 //开始兼听端口 //同时启动一个兼听进程 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("兼听端口时发生错误 :" + e.ToString()); } } public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket mySocket) { String sBuffer = ""; if (sMIMEHeader.Length == 0) { sMIMEHeader = "text/html"; // 默认 text/html } sBuffer = sBuffer + sHttpVersion + sStatusCode + " "; sBuffer = sBuffer + "Server: cx1193719-b "; sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + " "; sBuffer = sBuffer + "Accept-Ranges: bytes "; sBuffer = sBuffer + "Content-Length: " + iTotBytes + " "; Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer); SendToBrowser(bSendData, ref mySocket); Console.WriteLine("Total Bytes : " + iTotBytes.ToString()); } public void SendToBrowser(String sData, ref Socket mySocket) { SendToBrowser(Encoding.ASCII.GetBytes(sData), ref mySocket); } public void SendToBrowser(Byte[] bSendData, ref Socket mySocket) { int numBytes = 0; try { if (mySocket.Connected) { if ((numBytes = mySocket.Send(bSendData, bSendData.Length, 0)) == -1) Console.WriteLine("Socket Error cannot Send Packet"); else { Console.WriteLine("No. of bytes send {0}", numBytes); } } else Console.WriteLine("连接失败...."); } catch (Exception e) { Console.WriteLine("发生错误 : {0} ", e); } } public void StartListen() { int iStartPos = 0; String sRequest; String sDirName; String sRequestedFile; String sErrorMessage; String sLocalDir; /////////////////////////////////////注意设定你自己的虚拟目录///////////////////////////////////// String sMyWebServerRoot = "C:\Cassini"; //设置你的虚拟目录 ////////////////////////// String sFormattedMessage = ""; String sResponse = ""; while (true) { //接受新连接 Socket mySocket = myListener.AcceptSocket(); Console.WriteLine("Socket Type " + mySocket.SocketType); if (mySocket.Connected) { Console.WriteLine(" Client Connected!! ================== CLient IP {0} ", mySocket.RemoteEndPoint); Byte[] bReceive = new Byte[1024]; int i = mySocket.Receive(bReceive, bReceive.Length, 0); //转换成字符串类型 string sBuffer = Encoding.ASCII.GetString(bReceive); //只处理"get"请求类型 if (sBuffer.Substring(0, 3) != "GET") { Console.WriteLine("只处理get请求类型.."); mySocket.Close(); return; } // 查找 "HTTP" 的位置 iStartPos = sBuffer.IndexOf("HTTP", 1); string sHttpVersion = sBuffer.Substring(iStartPos, 8); // 得到请求类型和文件目录文件名 sRequest = sBuffer.Substring(0, iStartPos - 1); sRequest.Replace("", "/"); //如果结尾不是文件名也不是以"/"结尾则加"/" if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/"))) { sRequest = sRequest + "/"; } //得带请求文件名 iStartPos = sRequest.LastIndexOf("/") + 1; sRequestedFile = sRequest.Substring(iStartPos); //得到请求文件目录 sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/") - 3); //获取虚拟目录物理路径 sLocalDir = sMyWebServerRoot; Console.WriteLine("请求文件目录 : " + sLocalDir); if (sLocalDir.Length == 0) { sErrorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>"; SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket); SendToBrowser(sErrorMessage, ref mySocket); mySocket.Close(); continue; } if (sRequestedFile.Length == 0) { // 取得请求文件名 sRequestedFile = "index.html"; } ///////////////////////////////////////////////////////////////////// // 取得请求文件类型(设定为text/html) ///////////////////////////////////////////////////////////////////// String sMimeType = "text/html"; string sPhysicalFilePath = sLocalDir + sRequestedFile; Console.WriteLine("请求文件: " + sPhysicalFilePath); if (File.Exists(sPhysicalFilePath) == false) { sErrorMessage = "<script language='javascript'>alert('你好呀,我不是IIS服务器!');</script>"; //SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket); //SendToBrowser(sErrorMessage, ref mySocket); byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(sErrorMessage); SendHeader(sHttpVersion, sMimeType, bytes.Length, " 200 OK", ref mySocket); SendToBrowser(bytes, ref mySocket); //Console.WriteLine(sFormattedMessage); } else { int iTotBytes = 0; sResponse = ""; FileStream fs = new FileStream(sPhysicalFilePath, 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) { sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read); iTotBytes = iTotBytes + read; } reader.Close(); fs.Close(); SendHeader(sHttpVersion, sMimeType, iTotBytes, " 200 OK", ref mySocket); SendToBrowser(bytes, ref mySocket); //mySocket.Send(bytes, bytes.Length,0); } mySocket.Close(); } } } } }
四、编辑Program.cs,启动监听
static void Main(string[] args) { MyWebServer MWS = new MyWebServer(); Console.Read(); }