生活中充满了各种网站,那么网站的基础HTTP协议是什么样的,平时都是使用网页,或者看书上说HTTP协议。最近讲实现HTTP到Webservice系列协议的简单程序,并且开发代码,供爱学习的人理解HTTP协议到Webservice协议。
本次开发使用NetCore5.0,实现的服务可以运行在linux和windows。原理通了只要有精力任何有tcp的开发语言都能自己实现http服务器,不限于C#,可以用C、C++、Java、等等自己实现HTTP服务器。目前比较简单,抓紧学习(哈哈)
代码地址:https://download.csdn.net/download/zhanglianzhu_91/34029518
linux测试
[root@localhost httpd]# ll
总用量 200
-rw-r--r--. 1 root root 14848 10月 23 04:37 BaseHttpHandler.dll
-rw-r--r--. 1 root root 16124 10月 23 04:37 BaseHttpHandler.pdb
drwxr-xr-x. 2 root root 100 10月 23 04:31 htdocs
-rw-r--r--. 1 root root 713 10月 23 04:37 httpd.deps.json
-rw-r--r--. 1 root root 4608 10月 23 04:37 httpd.dll
-rw-r--r--. 1 root root 125952 10月 23 04:37 httpd.exe
-rw-r--r--. 1 root root 9732 10月 23 04:37 httpd.pdb
-rw-r--r--. 1 root root 317 10月 23 04:37 httpd.runtimeconfig.dev.json
-rw-r--r--. 1 root root 147 10月 23 04:37 httpd.runtimeconfig.json
-rw-r--r--. 1 root root 4460 10月 23 04:37 MIME.conf
drwxr-xr-x. 2 root root 23 10月 23 04:31 ref
-rw-r--r--. 1 root root 4096 10月 23 04:37 UserDeal.dll
[root@localhost httpd]# dotnet httpd.dll
如果要设定IP请输入,否则回车:
172.25.137.27
04:38:47:启动httpd服务器
04:38:47:启动TCP服务成功
04:38:47:启动在:172.25.137.27:50003
主要源码
程序入口
using BaseHttpHandler;
using System;
namespace httpd
{
class Program
{
static void Main(string[] args)
{
HttpdServer server = new HttpdServer();
string err;
Util.WriteLog("启动httpd服务器");
bool ret=server.StartServer(50003, out err);
if(err!="")
{
Util.WriteLog("启动服务失败:"+err);
}
Console.ReadLine();
}
}
}
http服务类-启动端口侦听等
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
namespace BaseHttpHandler
{
///
/// [功能描述:zlz实现的http服务类,实现http服务器]
/// [创建者:zlz]
/// [创建时间:zlz实现的http服务类,实现http服务器]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class HttpdServer
{
///
/// 网络服务对象
///
Socket serverSocket = null;
///
/// 启动http服务
///
/// 端口
/// 错误
/// 是否启动成功
public bool StartServer(int port, out string err)
{
err = "";
#region 读取MIME配置
//读取mime配置
string mimename = Path.Combine(AppContext.BaseDirectory, "MIME.conf");
Util.DicMime.Clear();
//解析mime
if (File.Exists(mimename))
{
StreamReader sr = new StreamReader(mimename, Encoding.UTF8);
string line;
while ((line = sr.ReadLine()) != null)
{
if (line != "")
{
string[] mimeArr = line.Split('^');
if (mimeArr.Length == 2 && (!Util.DicMime.ContainsKey("." + mimeArr[0])))
{
Util.DicMime.Add("." + mimeArr[0], mimeArr[1]);
}
}
}
sr.Close();
sr.Dispose();
}
#endregion
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//得到当前电脑IP
IPAddress hostIP = Util.GetIpv4Str();
//判断端口是否被占用
if (Util.PortInUse(port))
{
err = "当前电脑端口:" + port + "已经被占用!";
Util.WriteLog(err);
return false;
}
//创建终节点
IPEndPoint endPoint = new IPEndPoint(hostIP, port);
serverSocket.Bind(endPoint);
try
{
//开始侦听
serverSocket.Listen(200);
Util.WriteLog("启动TCP服务成功");
Util.WriteLog("启动在:" + hostIP.ToString() + ":" + port);
Thread tcpThread = new Thread(new ThreadStart(ServerTcpListenMain));
tcpThread.Start();
}
catch (Exception ex)
{
err = ex.Message;
Util.WriteLog(err);
return false;
}
return true;
}
///
/// 服务端侦听主线程,用死循环侦听服务端口收到的连接
///
private void ServerTcpListenMain()
{
while (true)
{
try
{
//有新连接过来,开启子进程处理连接
Socket client = serverSocket.Accept();
if (client != null)
{
//用新线程处理请求
DealClientRequest deal = new DealClientRequest(client);
}
}
catch (Exception ex)
{
Util.WriteLog("服务端主侦听异常:" + ex.Message);
}
}
}
}
}
请求处理类-开启请求处理线程和请求解析
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BaseHttpHandler
{
///
/// [功能描述:用来处理客户端的请求]
/// [创建者:zlz]
/// [创建时间:用来处理客户端的请求]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class DealClientRequest
{
///
/// 客户端
///
private Socket client = null;
///
/// 构造函数
///
///
public DealClientRequest(Socket Client)
{
client = Client;
Thread newThread = new Thread(DealRequest);
newThread.Start();
}
///
/// 客户端服务
///
public void DealRequest()
{
string data = null;
byte[] bytes = new byte[4096];
try
{
StringBuilder sb = new StringBuilder();
int dataLen = 0;
client.ReceiveTimeout = 2000;
while ((dataLen = client.Receive(bytes)) != 0 || sb.Length == 0)
{
if (dataLen < 0)
{
break;
}
data = System.Text.Encoding.Default.GetString(bytes, 0, dataLen);
sb.Append(data);
if (client.Available == 0)
{
break;
}
}
string allStr = sb.ToString();
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:收到:") + allStr);
HttpContex contex = new HttpContex(allStr, client);
HttpHandler handler = new HttpHandler();
handler.Invok(contex);
}
catch (System.Exception ex)
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:处理客户端请求异常,") + ex.Message);
}
finally
{
if (client != null)
{
client.Close();
}
}
}
}
}
http主处理
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Loader;
namespace BaseHttpHandler
{
///
/// [功能描述:Http执行句柄]
/// [创建者:zlz]
/// [创建时间:Http执行句柄]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class HttpHandler
{
///
/// http上下文
///
public HttpContex Contex;
///
/// 执行请求
///
///
public void Invok(HttpContex contex)
{
Contex = contex;
//是POST类型的请求
if (contex.HRequest.GetReqType() == "POST")
{
string path = contex.HRequest.GetReqPath();
string url = path.Split('?')[0];
string[] arr = url.Split('/');
if (arr.Length > 2)
{
string dllName = arr[1];
string classFullName = arr[2];
string ret = ProcessRequestReflect(dllName, classFullName);
contex.HResponse.Write(ret);
}
else
{
contex.HResponse.Write("-1^错误的请求路径!");
}
}
}
///
/// 执行方法
///
///
///
///
private string ProcessRequestReflect(string dllName, string className)
{
dllName = Path.Combine(AppContext.BaseDirectory, dllName + ".dll");
Assembly assembly = Assembly.LoadFrom(dllName);
//创建实例
object obj = assembly.CreateInstance(className, false);
if (obj != null)
{
Util.SetPropertyValue(obj, "Contex", Contex);
//获得前台传入的要调用的方法名称
string method = Contex.HRequest.GetReqPara("Method");
//方法名称不为空,找到并执行方法
if (!string.IsNullOrEmpty(method))
{
//根据名称获得方法信息
MethodInfo methodInfo = obj.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
//没找到方法就抛出错误信息
if (methodInfo == null)
{
return "-1^未找到后台方法“" + method + "”!";
}
object ret = methodInfo.Invoke(obj, null);
if (ret == null)
{
return null;
}
//执行找到的方法,并返回结果
return (string)ret;
}
else
{
return "-1^方法名Method不能为空!";
}
}
else
{
return "-1^处理类不存在!";
}
}
}
}
上下文对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace BaseHttpHandler
{
///
/// [功能描述:包装简单的上下文对象,用来响应客户端的消息]
/// [创建者:zlz]
/// [创建时间:包装简单的上下文对象,用来响应来着客户端的消息]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class HttpContex
{
///
/// 请求对象
///
public Request HRequest
{
get;
set;
}
///
/// 响应对象
///
public Response HResponse
{
get;
set;
}
///
/// 构造函数
///
///
///
public HttpContex(string data, Socket Client)
{
HRequest = new Request(data, Client);
HResponse = new Response(data, Client);
}
}
}
Request对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net.Sockets;
namespace BaseHttpHandler
{
///
/// [功能描述:包装简单的请求对象,用来处理来着客户端的消息]
/// [创建者:zlz]
/// [创建时间:包装简单的请求对象,用来处理来着客户端的消息]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class Request
{
///
/// 客户端
///
private Socket client = null;
///
/// 客户端发送的数据行
///
string[] dataArr = null;
///
/// 请求类型GET POST
///
private string type = "";
///
/// 请求的相对地址
///
private string reqPath = "";
///
/// http协议版本
///
private string httpVertion = "";
///
/// 头信息字典
///
Dictionary<string, string> headDic = new Dictionary<string, string>();
///
/// 存Post的串
///
private string PostStr = "";
///
/// 存Url的参数串
///
private string UrlPara = "";
///
/// 存参数字典
///
private Dictionary<string, string> paraDic = new Dictionary<string, string>();
///
/// Request对象构造函数
///
///
/// 客户端
public Request(string data, Socket Client)
{
client = Client;
//分隔数据
if (data != "")
{
dataArr = data.Split("\r\n");
int i = 0;
for (; i < dataArr.Length; i++)
{
string oneRow = dataArr[i];
if (i == 0)
{
string[] typeArr = oneRow.Split(' ');
if (typeArr.Length > 2)
{
type = typeArr[0].ToUpper();
reqPath = typeArr[1];
if (reqPath.Contains('?'))
{
string[] arr = reqPath.Split('?');
reqPath = arr[0];
if (arr.Length > 1)
{
UrlPara = arr[1];
}
}
httpVertion = typeArr[2];
}
}
if (type == "POST" && oneRow == "")
{
break;
}
else
{
string[] typeArr = oneRow.Split(':');
if (typeArr.Length > 1)
{
headDic.Add(typeArr[0], typeArr[1]);
}
}
}
//提取POST串
if (i < dataArr.Length)
{
int pIndex = 0;
for (; i < dataArr.Length; i++)
{
string oneRow = dataArr[i];
if (pIndex == 0)
{
PostStr += oneRow;
}
else
{
PostStr += "\r\n" + oneRow;
}
pIndex++;
}
}
}
if (type == "GET")
{
DealGet(reqPath);
}
}
///
/// 处理GET请求,响应资源
///
/// 资源相对路径
///
private string DealGet(string path)
{
string filePath = path;
//定位首页
if (filePath == "/" || filePath == "")
{
filePath = "Index.html";
}
string[] fileArr = filePath.Split('/');
string fullPath = Util.htdocs;
//拼成全路径
foreach (var f in fileArr)
{
fullPath = Path.Combine(fullPath, f);
}
//请求的资源不存在
if (!File.Exists(fullPath))
{
NotFund();
}
//发送文件资源串
else
{
SenFile(fullPath);
}
return "";
}
///
/// 发送文件
///
///
public void SenFile(string fileFullName)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Server: zlzhttpd/0.0.1");
#region 按文件后缀返回MIME类型
FileInfo fi = new FileInfo(fileFullName);
string Mime;
if (Util.DicMime.ContainsKey(fi.Extension))
{
Mime = Util.DicMime[fi.Extension];
sb.AppendLine("Content-Type: " + Util.DicMime[fi.Extension]);
}
else
{
Mime = "text/html";
sb.AppendLine("Content-Type: text/html");
}
#endregion
sb.AppendLine("");
if (Mime.Contains("image/") || Mime.Contains("video/") || Mime.Contains("audio/") || Mime.Contains("application/pdf"))
{
Util.WriteToClient(client, sb.ToString());
client.SendFile(fileFullName);
return;
}
sb.AppendLine(Util.ReadTxt(fileFullName));
Util.WriteToClient(client, sb.ToString());
}
///
/// 发送未找到消息
///
private void NotFund()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 404 未找到资源");
sb.AppendLine("Content-Type: text/html");
sb.AppendLine("");
sb.AppendLine("没找到请求的资源 ");
sb.AppendLine("服务器不能响应你的请求,因为资源不可用或者未找到"
);
sb.AppendLine("");
sb.AppendLine("");
Util.WriteToClient(client, sb.ToString());
}
///
/// 得到头信息
///
/// 名称
///
public string GetHeadStr(string name)
{
if (headDic.ContainsKey(name))
{
return headDic[name];
}
return "";
}
///
/// 得到请求类型
///
///
public string GetReqType()
{
return type;
}
///
/// 得到请求路径
///
///
public string GetReqPath()
{
return reqPath;
}
///
/// 得到http协议版本
///
///
public string GetHttpVertion()
{
return httpVertion;
}
///
/// 得到请求参数
///
/// 参数名称
/// 请求参数
public string GetReqPara(string name)
{
if (name == null || name == "")
{
return "";
}
//构造参数字典
if (paraDic.Count == 0)
{
if (PostStr != "" && UrlPara != "")
{
string AllPara = UrlPara + "&" + PostStr;
string[] arr = AllPara.Split('&');
if (arr != null && arr.Length > 0)
{
foreach (var v in arr)
{
if (v != "")
{
string[] arrone = v.Split('=');
if (arrone != null && arrone.Length > 1)
{
arrone[0] = arrone[0].Replace("\r\n", "");
if (arrone[0] != "")
{
if (!paraDic.ContainsKey(arrone[0]))
{
paraDic.Add(arrone[0], arrone[1]);
}
}
}
}
}
}
}
}
if (paraDic.ContainsKey(name))
{
return paraDic[name];
}
return "";
}
}
}
Response对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace BaseHttpHandler
{
///
/// [功能描述:包装简单的响应对象,用来响应客户端的消息]
/// [创建者:zlz]
/// [创建时间:包装简单的响应对象,用来响应来着客户端的消息]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class Response
{
///
/// 客户端
///
private Socket client = null;
///
/// 客户端发送的数据行
///
string[] dataArr = null;
///
/// 请求类型GET POST
///
private string type = "";
///
/// 请求的相对地址
///
private string reqPath = "";
///
/// http协议版本
///
private string httpVertion = "";
///
/// 头信息字典
///
Dictionary<string, string> headDic = new Dictionary<string, string>();
///
/// Request对象构造函数
///
///
/// 客户端
public Response(string data, Socket Client)
{
client = Client;
//分隔数据
if (data != "")
{
dataArr = data.Split("\r\n");
for (int i = 0; i < dataArr.Length; i++)
{
string oneRow = dataArr[i];
if (i == 0)
{
string[] typeArr = oneRow.Split(' ');
if (typeArr.Length > 2)
{
type = typeArr[0];
reqPath = typeArr[1];
httpVertion = typeArr[2];
}
}
else
{
string[] typeArr = oneRow.Split(':');
if (typeArr.Length > 1)
{
headDic.Add(typeArr[0], typeArr[1]);
}
}
}
}
}
///
/// 给客户端写信息
///
///
public void Write(string msg)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Server: zlzhttpd/0.0.1");
sb.AppendLine("Content-Type: text/html");
sb.AppendLine("");
sb.AppendLine(msg);
Util.WriteToClient(client, sb.ToString());
}
}
}
处理类基类-所有处理类继承该类就能Ajax请求
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BaseHttpHandler
{
///
/// [功能描述:所有后台处理类继承的基类,通过该类的Context对象操作]
/// [创建者:zlz]
/// [创建时间:所有后台处理类继承的基类,通过该类的Context对象操作]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class HttpDealBase
{
///
/// http上下文
///
public HttpContex Contex
{
get;
set;
}
}
}
工具类-各种工具支持
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net.Sockets;
using System.Reflection;
namespace BaseHttpHandler
{
///
/// [功能描述:工具类]
/// [创建者:zlz]
/// [创建时间:工具类]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class Util
{
///
/// 资源根路径
///
public static string htdocs = Path.Combine(AppContext.BaseDirectory, "htdocs");
///
/// 存MIME
///
public static Dictionary<string, string> DicMime = new Dictionary<string, string>();
///
/// 给客户端写消息
///
/// 客户端
/// 消息
///
public static bool WriteToClient(Socket Client, string msg)
{
byte[] byteArray = System.Text.Encoding.Default.GetBytes(msg);
Client.Send(byteArray);
WriteLog("发送:" + msg);
return true;
}
///
/// 写日志
///
/// 日志
public static void WriteLog(string log)
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:") + log);
}
///
/// 获得当前机器的ip4串
///
///
public static IPAddress GetIpv4Str()
{
System.Net.IPAddress[] arrIPAddresses = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName());
foreach (System.Net.IPAddress ip in arrIPAddresses)
{
if (ip.AddressFamily.Equals(System.Net.Sockets.AddressFamily.InterNetwork))
{
return ip;
}
}
return null;
}
///
/// 判断端口是否被占用
///
/// 端口
///
public static bool PortInUse(int port)
{
bool inUse = false;
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
inUse = true;
break;
}
}
return inUse;
}
///
/// 读取文件数据
///
/// 文件全路径
///
public static string ReadTxt(string path)
{
//文件不存在
if (!File.Exists(path))
{
return "";
}
FileStream fs = null;
try
{
fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader sr = new StreamReader(fs, GetFileEncodeType(fs));
string str = sr.ReadToEnd();
return str;
}
catch (Exception ex)
{
return "";
}
finally
{
fs.Close();
}
}
///
/// 获得编码格式
///
///
///
private static Encoding GetFileEncodeType(FileStream fs)
{
byte[] Unicode = new byte[] {
0xFF, 0xFE, 0x41 };
byte[] UnicodeBIG = new byte[] {
0xFE, 0xFF, 0x00 };
byte[] UTF8 = new byte[] {
0xEF, 0xBB, 0xBF }; //带BOM
Encoding reVal = Encoding.Default;
BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);
int i;
int.TryParse(fs.Length.ToString(), out i);
byte[] ss = r.ReadBytes(i);
if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
{
reVal = Encoding.UTF8;
}
else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
{
reVal = Encoding.BigEndianUnicode;
}
else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
{
reVal = Encoding.Unicode;
}
fs.Position = 0;
return reVal;
}
///
/// 给对象属性赋值
///
/// 对象
/// 属性
/// 结果值
public static void SetPropertyValue(Object obj, string name, Object value)
{
PropertyInfo property = obj.GetType().GetProperty(name);
//将值设置到对象中
property.SetValue(obj, value, null);
}
///
/// 判断是否是不带 BOM 的 UTF8 格式
///
///
///
private static bool IsUTF8Bytes(byte[] data)
{
int charByteCounter = 1; //计算当前正分析的字符应还有的字节数
byte curByte; //当前分析的字节.
for (int i = 0; i < data.Length; i++)
{
curByte = data[i];
if (charByteCounter == 1)
{
if (curByte >= 0x80)
{
//判断当前
while (((curByte <<= 1) & 0x80) != 0)
{
charByteCounter++;
}
//标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X
if (charByteCounter == 1 || charByteCounter > 6)
{
return false;
}
}
}
else
{
//若是UTF-8 此时第一位必须为1
if ((curByte & 0xC0) != 0x80)
{
return false;
}
charByteCounter--;
}
}
if (charByteCounter > 1)
{
throw new Exception("非预期的byte格式!");
}
return true;
}
}
}
处理类示例-供ajax请求
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BaseHttpHandler
{
///
/// [功能描述:默认提供的HTTP处理类示例]
/// [创建者:zlz]
/// [创建时间:默认提供的HTTP处理类示例]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class DealDemo : HttpDealBase
{
///
/// 获得当前时间
///
///
public string GetNowTime()
{
return "当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}
处理类示例-供ajax请求
using BaseHttpHandler;
using System;
namespace UserDeal
{
///
/// [功能描述:用户处理类实现]
/// [创建者:zlz]
/// [创建时间:用户处理类实现]
///<说明>
/// [说明:]
///说明>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///<修改记录>
/// [修改时间:本次修改时间]
/// [修改内容:本次修改内容]
///修改记录>
///
public class DealClass : HttpDealBase
{
///
/// 得到服务器信息
///
///
public string GetServerInfo()
{
return "这是C#基于TCP实现的HTPP服务器,版本信息:" + Contex.HRequest.GetHttpVertion() + " 前台给我提交了Para:" + Contex.HRequest.GetReqPara("Para") + " Para1:" + Contex.HRequest.GetReqPara("Para1") + " UserName:" + Contex.HRequest.GetReqPara("UserName");
}
}
}
mime配置-按请求后缀返回mime类型
aspx^text/html
html^text/html
htm^text/html
dll^application/x-msdownload
dll.config^text/xml
xml^text/xml
config^text/xml
doc^application/msword
docx^application/vnd.openxmlformats-officedocument.wordprocessingml.document
xls^application/vnd.ms-excel
xlsx^application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
323^text/h323
acx^application/internet-property-stream
ai^application/postscript
aif^audio/x-aiff
aifc^audio/x-aiff
aiff^audio/x-aiff
asf^video/x-ms-asf
asr^video/x-ms-asf
asx^video/x-ms-asf
au^audio/basic
avi^video/x-msvideo
axs^application/olescript
bas^text/plain
bcpio^application/x-bcpio
bin^application/octet-stream
bmp^image/bmp
c^text/plain
cat^application/vnd.ms-pkiseccat
cdf^application/x-cdf
cer^application/x-x509-ca-cert
class^application/octet-stream
clp^application/x-msclip
cmx^image/x-cmx
cod^image/cis-cod
cpio^application/x-cpio
crd^application/x-mscardfile
crl^application/pkix-crl
crt^application/x-x509-ca-cert
csh^application/x-csh
css^text/css
dcr^application/x-director
der^application/x-x509-ca-cert
dir^application/x-director
dms^application/octet-stream
doc^application/msword
dot^application/msword
dvi^application/x-dvi
dxr^application/x-director
eps^application/postscript
etx^text/x-setext
evy^application/envoy
exe^application/octet-stream
fif^application/fractals
flr^x-world/x-vrml
gif^image/gif
gtar^application/x-gtar
gz^application/x-gzip
rar^application/octet-stream
zip^application/x-zip-compressed
h^text/plain
hdf^application/x-hdf
hlp^application/winhlp
hqx^application/mac-binhex40
hta^application/hta
htc^text/x-component
htm^text/html
html^text/html
htt^text/webviewhtml
ico^image/x-icon
ICO^image/x-icon
ief^image/ief
woff^font/x-woff
iii^application/x-iphone
ins^application/x-internet-signup
isp^application/x-internet-signup
jfif^image/pipeg
jpe^image/jpeg
jpeg^image/jpeg
jpg^image/jpeg
js^application/x-javascript
json^application/x-javascript
latex^application/x-latex
lha^application/octet-stream
lsf^video/x-la-asf
lsx^video/x-la-asf
lzh^application/octet-stream
m13^application/x-msmediaview
m14^application/x-msmediaview
m3u^audio/x-mpegurl
man^application/x-troff-man
mdb^application/x-msaccess
me^application/x-troff-me
mht^message/rfc822
mhtml^message/rfc822
mid^audio/mid
mny^application/x-msmoney
mov^video/Quicktime
movie^video/x-sgi-movie
mp2^video/mpeg
mp3^audio/mpeg
mpa^video/mpeg
mpe^video/mpeg
mpeg^video/mpeg
mpg^video/mpeg
mpp^application/vnd.ms-project
mpv2^video/mpeg
ms^application/x-troff-ms
mvb^application/x-msmediaview
nws^message/rfc822
oda^application/oda
p10^application/pkcs10
p12^application/x-pkcs12
p7b^application/x-pkcs7-certificates
p7c^application/x-pkcs7-mime
p7m^application/x-pkcs7-mime
p7r^application/x-pkcs7-certreqresp
p7s^application/x-pkcs7-signature
pbm^image/x-portable-bitmap
pdf^application/pdf
pfx^application/x-pkcs12
pgm^image/x-portable-graymap
pko^application/ynd.ms-pkipko
pma^application/x-perfmon
pmc^application/x-perfmon
pml^application/x-perfmon
pmr^application/x-perfmon
pmw^application/x-perfmon
pnm^image/x-portable-anymap
pot,^application/vnd.ms-powerpoint
ppm^image/x-portable-pixmap
pps^application/vnd.ms-powerpoint
ppt^application/vnd.ms-powerpoint
prf^application/pics-rules
ps^application/postscript
pub^application/x-mspublisher
qt^video/quicktime
ra^audio/x-pn-realaudio
ram^audio/x-pn-realaudio
ras^image/x-cmu-raster
rgb^image/x-rgb
rmi^audio/mid
roff^application/x-troff
rtf^application/rtf
rtx^text/richtext
scd^application/x-msschedule
sct^text/scriptlet
setpay^application/set-payment-initiation
setreg^application/set-registration-initiation
sh^application/x-sh
shar^application/x-shar
sit^application/x-stuffit
snd^audio/basic
spc^application/x-pkcs7-certificates
spl^application/futuresplash
src^application/x-wais-source
sst^application/vnd.ms-pkicertstore
stl^application/vnd.ms-pkistl
stm^text/html
svg^image/svg+xml
sv4cpio^application/x-sv4cpio
sv4crc^application/x-sv4crc
swf^application/x-shockwave-flash
t^application/x-troff
tar^application/x-tar
tcl^application/x-tcl
tex^application/x-tex
texi^application/x-texinfo
texinfo^application/x-texinfo
tgz^application/x-compressed
tif^image/tiff
tiff^image/tiff
tr^application/x-troff
trm^application/x-msterminal
tsv^text/tab-separated-values
txt^text/plain
png^image/png
html示例
doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta charset="utf-8">
<title>ZLZ的HTPP服务器练习title>
<link href="Index.css" rel="stylesheet" type="text/css" />
<script src="jquery-2.1.1.js" type="text/javascript">script>
<script type="text/javascript">
$(function () {
$("#divInfo").html("这是我用单独加载的CSS文件控制的边框样式,通过JQuery用JS代码设置的内容,验证CSS和JS加载");
//请求服务
$.ajax({
type: "POST",
dataType: "text", //text, json, xml
cache: false, //
async: true, //为true时,异步,不等待后台返回值,为false时强制等待;-asir
url: '/BaseHttpHandler/BaseHttpHandler.DealDemo?Method=GetNowTime',
data:{
Para:"Post参数",Para1:"Post参数1"},
success: function (data) {
$("#divAjax1").html("这是Ajax请求后台服务类获得时间的测试:"+data);
}
});
//请求服务
$.ajax({
type: "POST",
dataType: "text", //text, json, xml
cache: false, //
async: true, //为true时,异步,不等待后台返回值,为false时强制等待;-asir
url: '/UserDeal/UserDeal.DealClass?Method=GetServerInfo&UserName=zhanglianzhu',
data: {
Para: "这是zlz提交的Post参数1", Para1: "Post参数2" },
success: function (data) {
$("#divAjax2").html("这是Ajax测试:" + data);
}
});
});
script>
<style type="text/css">
* {
box-sizing: border-box;
}
body {
font-family: 'Nunito', sans-serif;
background: -webkit-linear-gradient(right, #BE93C5, #7BC6CC);
background: linear-gradient(to left, #BE93C5, #7BC6CC);
}
img {
max-width: 100%;
height: auto;
}
.site-header {
text-align: center;
padding: 40px 0;
}
.site-header__title {
font-size: 36px;
color: #fff;
}
.wrapper {
padding-left: 18px;
padding-right: 18px;
max-width: 1236px;
margin-left: auto;
margin-right: auto;
}
.timeline {
position: relative;
margin: 30px auto;
padding: 60px 0;
}
.timeline::before {
content: "";
position: absolute;
top: 0;
left: 10%;
width: 4px;
height: 100%;
background-color: #8d94b1;
}
@media (min-width: 800px) {
.timeline::before {
left: 50%;
margin-left: -2px;
}
}
.timeline__item {
margin-bottom: 100px;
position: relative;
}
.timeline__item::after {
content: "";
clear: both;
display: table;
}
.timeline__item:nth-child(2n) .timeline__item__content {
float: right;
}
.timeline__item:nth-child(2n) .timeline__item__content::before {
content: '';
right: 40%;
}
@media (min-width: 800px) {
.timeline__item:nth-child(2n) .timeline__item__content::before {
left: inherit;
}
}
.timeline__item:nth-child(2n) .timeline__item__content__date {
background-color: #b292c5;
}
.timeline__item:nth-child(2n) .timeline__item__content__description {
color: #b292c5;
}
.timeline__item:last-child {
margin-bottom: 0;
}
.timeline__item-bg {
-webkit-transition: all 1s ease-out;
transition: all 1s ease-out;
color: #fff;
}
.timeline__item-bg:nth-child(2n) .timeline__item__station {
background-color: #b292c5;
}
.timeline__item-bg:nth-child(2n) .timeline__item__content {
background-color: #b292c5;
}
.timeline__item-bg:nth-child(2n) .timeline__item__content::before {
background-color: #b292c5;
}
.timeline__item-bg:nth-child(2n) .timeline__item__content__description {
color: #fff;
}
.timeline__item-bg .timeline__item__station {
background-color: #65adb7;
}
.timeline__item-bg .timeline__item__content {
background-color: #65adb7;
}
.timeline__item-bg .timeline__item__content::before {
background-color: #65adb7;
}
.timeline__item-bg .timeline__item__content__description {
color: #fff;
}
.timeline__item__station {
background-color: #9aa0b9;
width: 40px;
height: 40px;
position: absolute;
border-radius: 50%;
padding: 10px;
top: 0;
left: 10%;
margin-left: -33px;
border: 4px solid #8d94b1;
-webkit-transition: all .3s ease-out;
transition: all .3s ease-out;
}
@media (min-width: 800px) {
.timeline__item__station {
left: 50%;
margin-left: -30px;
width: 60px;
height: 60px;
padding: 15px;
border-width: 6px;
}
}
.timeline__item__content {
width: 80%;
background: #fff;
padding: 20px 30px;
border-radius: 6px;
float: right;
-webkit-transition: all .3s ease-out;
transition: all .3s ease-out;
}
@media (min-width: 800px) {
.timeline__item__content {
width: 40%;
float: inherit;
padding: 30px 40px;
}
}
.timeline__item__content::before {
content: '';
position: absolute;
left: 10%;
background: #8d94b1;
top: 20px;
width: 10%;
height: 4px;
z-index: -1;
-webkit-transition: all .3s ease-out;
transition: all .3s ease-out;
}
@media (min-width: 800px) {
.timeline__item__content::before {
left: 40%;
top: 30px;
height: 4px;
margin-top: -2px;
}
}
.timeline__item__content__date {
margin: 0;
padding: 8px 12px;
font-size: 15px;
margin-bottom: 10px;
background-color: #65adb7;
color: #fff;
display: inline-block;
border-radius: 4px;
border: 2px solid #fff;
}
.timeline__item__content__description {
margin: 0;
padding: 0;
font-size: 16px;
line-height: 24px;
font-weight: 300;
color: #65adb7;
}
@media (min-width: 800px) {
.timeline__item__content__description {
font-size: 19px;
line-height: 28px;
}
}
/* _site-footer.css */
.site-footer {
padding: 50px 0 200px 0;
}
.site-footer__text {
color: #e6e6e6;
font-size: 14px;
text-align: center;
}
.site-footer__text__link {
color: #8287a9;
}
style>
head>
<body>
<script type="text/javascript">
!function () {
"use strict"; function t(o) {
if (!o) throw new Error("No options passed to Waypoint constructor"); if (!o.element) throw new Error("No element option passed to Waypoint constructor"); if (!o.handler) throw new Error("No handler option passed to Waypoint constructor"); this.key = "waypoint-" + e, this.options = t.Adapter.extend({
}, t.defaults, o), this.element = this.options.element, this.adapter = new t.Adapter(this.element), this.callback = o.handler, this.axis = this.options.horizontal ? "horizontal" : "vertical", this.enabled = this.options.enabled, this.triggerPoint = null, this.group = t.Group.findOrCreate({
name: this.options.group, axis: this.axis }), this.context = t.Context.findOrCreateByElement(this.options.context), t.offsetAliases[this.options.offset] && (this.options.offset = t.offsetAliases[this.options.offset]), this.group.add(this), this.context.add(this), i[this.key] = this, e += 1 } var e = 0, i = {
}; t.prototype.queueTrigger = function (t) {
this.group.queueTrigger(this, t) }, t.prototype.trigger = function (t) {
this.enabled && this.callback && this.callback.apply(this, t) }, t.prototype.destroy = function () {
this.context.remove(this), this.group.remove(this), delete i[this.key] }, t.prototype.disable = function () {
return this.enabled = !1, this }, t.prototype.enable = function () {
return this.context.refresh(), this.enabled = !0, this }, t.prototype.next = function () {
return this.group.next(this) }, t.prototype.previous = function () {
return this.group.previous(this) }, t.invokeAll = function (t) {
var e = []; for (var o in i) e.push(i[o]); for (var n = 0, r = e.length; r > n; n++)e[n][t]() }, t.destroyAll = function () {
t.invokeAll("destroy") }, t.disableAll = function () {
t.invokeAll("disable") }, t.enableAll = function () {
t.invokeAll("enable") }, t.refreshAll = function () {
t.Context.refreshAll() }, t.viewportHeight = function () {
return window.innerHeight || document.documentElement.clientHeight }, t.viewportWidth = function () {
return document.documentElement.clientWidth }, t.adapters = [], t.defaults = {
context: window, continuous: !0, enabled: !0, group: "default", horizontal: !1, offset: 0 }, t.offsetAliases = {
"bottom-in-view": function () {
return this.context.innerHeight() - this.adapter.outerHeight() }, "right-in-view": function () {
return this.context.innerWidth() - this.adapter.outerWidth() } }, window.Waypoint = t }(), function () {
"use strict"; function t(t) {
window.setTimeout(t, 1e3 / 60) } function e(t) {
this.element = t, this.Adapter = n.Adapter, this.adapter = new this.Adapter(t), this.key = "waypoint-context-" + i, this.didScroll = !1, this.didResize = !1, this.oldScroll = {
x: this.adapter.scrollLeft(), y: this.adapter.scrollTop() }, this.waypoints = {
vertical: {
}, horizontal: {
} }, t.waypointContextKey = this.key, o[t.waypointContextKey] = this, i += 1, this.createThrottledScrollHandler(), this.createThrottledResizeHandler() } var i = 0, o = {
}, n = window.Waypoint, r = window.onload; e.prototype.add = function (t) {
var e = t.options.horizontal ? "horizontal" : "vertical"; this.waypoints[e][t.key] = t, this.refresh() }, e.prototype.checkEmpty = function () {
var t = this.Adapter.isEmptyObject(this.waypoints.horizontal), e = this.Adapter.isEmptyObject(this.waypoints.vertical); t && e && (this.adapter.off(".waypoints"), delete o[this.key]) }, e.prototype.createThrottledResizeHandler = function () {
function t() {
e.handleResize(), e.didResize = !1 } var e = this; this.adapter.on("resize.waypoints", function () {
e.didResize || (e.didResize = !0, n.requestAnimationFrame(t)) }) }, e.prototype.createThrottledScrollHandler = function () {
function t() {
e.handleScroll(), e.didScroll = !1 } var e = this; this.adapter.on("scroll.waypoints", function () {
(!e.didScroll || n.isTouch) && (e.didScroll = !0, n.requestAnimationFrame(t)) }) }, e.prototype.handleResize = function () {
n.Context.refreshAll() }, e.prototype.handleScroll = function () {
var t = {
}, e = {
horizontal: {
newScroll: this.adapter.scrollLeft(), oldScroll: this.oldScroll.x, forward: "right", backward: "left" }, vertical: {
newScroll: this.adapter.scrollTop(), oldScroll: this.oldScroll.y, forward: "down", backward: "up" } }; for (var i in e) {
var o = e[i], n = o.newScroll > o.oldScroll, r = n ? o.forward : o.backward; for (var s in this.waypoints[i]) {
var a = this.waypoints[i][s], l = o.oldScroll < a.triggerPoint, h = o.newScroll >= a.triggerPoint, p = l && h, u = !l && !h; (p || u) && (a.queueTrigger(r), t[a.group.id] = a.group) } } for (var c in t) t[c].flushTriggers(); this.oldScroll = {
x: e.horizontal.newScroll, y: e.vertical.newScroll } }, e.prototype.innerHeight = function () {
return this.element == this.element.window ? n.viewportHeight() : this.adapter.innerHeight() }, e.prototype.remove = function (t) {
delete this.waypoints[t.axis][t.key], this.checkEmpty() }, e.prototype.innerWidth = function () {
return this.element == this.element.window ? n.viewportWidth() : this.adapter.innerWidth() }, e.prototype.destroy = function () {
var t = []; for (var e in this.waypoints) for (var i in this.waypoints[e]) t.push(this.waypoints[e][i]); for (var o = 0, n = t.length; n > o; o++)t[o].destroy() }, e.prototype.refresh = function () {
var t, e = this.element == this.element.window, i = e ? void 0 : this.adapter.offset(), o = {
}; this.handleScroll(), t = {
horizontal: {
contextOffset: e ? 0 : i.left, contextScroll: e ? 0 : this.oldScroll.x, contextDimension: this.innerWidth(), oldScroll: this.oldScroll.x, forward: "right", backward: "left", offsetProp: "left" }, vertical: {
contextOffset: e ? 0 : i.top, contextScroll: e ? 0 : this.oldScroll.y, contextDimension: this.innerHeight(), oldScroll: this.oldScroll.y, forward: "down", backward: "up", offsetProp: "top" } }; for (var r in t) {
var s = t[r]; for (var a in this.waypoints[r]) {
var l, h, p, u, c, d = this.waypoints[r][a], f = d.options.offset, w = d.triggerPoint, y = 0, g = null == w; d.element !== d.element.window && (y = d.adapter.offset()[s.offsetProp]), "function" == typeof f ? f = f.apply(d) : "string" == typeof f && (f = parseFloat(f), d.options.offset.indexOf("%") > -1 && (f = Math.ceil(s.contextDimension * f / 100))), l = s.contextScroll - s.contextOffset, d.triggerPoint = y + l - f, h = w < s.oldScroll, p = d.triggerPoint >= s.oldScroll, u = h && p, c = !h && !p, !g && u ? (d.queueTrigger(s.backward), o[d.group.id] = d.group) : !g && c ? (d.queueTrigger(s.forward), o[d.group.id] = d.group) : g && s.oldScroll >= d.triggerPoint && (d.queueTrigger(s.forward), o[d.group.id] = d.group) } } return n.requestAnimationFrame(function () {
for (var t in o) o[t].flushTriggers() }), this }, e.findOrCreateByElement = function (t) {
return e.findByElement(t) || new e(t) }, e.refreshAll = function () {
for (var t in o) o[t].refresh() }, e.findByElement = function (t) {
return o[t.waypointContextKey] }, window.onload = function () {
r && r(), e.refreshAll() }, n.requestAnimationFrame = function (e) {
var i = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || t; i.call(window, e) }, n.Context = e }(), function () {
"use strict"; function t(t, e) {
return t.triggerPoint - e.triggerPoint } function e(t, e) {
return e.triggerPoint - t.triggerPoint } function i(t) {
this.name = t.name, this.axis = t.axis, this.id = this.name + "-" + this.axis, this.waypoints = [], this.clearTriggerQueues(), o[this.axis][this.name] = this } var o = {
vertical: {
}, horizontal: {
} }, n = window.Waypoint; i.prototype.add = function (t) {
this.waypoints.push(t) }, i.prototype.clearTriggerQueues = function () {
this.triggerQueues = {
up: [], down: [], left: [], right: [] } }, i.prototype.flushTriggers = function () {
for (var i in this.triggerQueues) {
var o = this.triggerQueues[i], n = "up" === i || "left" === i; o.sort(n ? e : t); for (var r = 0, s = o.length; s > r; r += 1) {
var a = o[r]; (a.options.continuous || r === o.length - 1) && a.trigger([i]) } } this.clearTriggerQueues() }, i.prototype.next = function (e) {
this.waypoints.sort(t); var i = n.Adapter.inArray(e, this.waypoints), o = i === this.waypoints.length - 1; return o ? null : this.waypoints[i + 1] }, i.prototype.previous = function (e) {
this.waypoints.sort(t); var i = n.Adapter.inArray(e, this.waypoints); return i ? this.waypoints[i - 1] : null }, i.prototype.queueTrigger = function (t, e) {
this.triggerQueues[e].push(t) }, i.prototype.remove = function (t) {
var e = n.Adapter.inArray(t, this.waypoints); e > -1 && this.waypoints.splice(e, 1) }, i.prototype.first = function () {
return this.waypoints[0] }, i.prototype.last = function () {
return this.waypoints[this.waypoints.length - 1] }, i.findOrCreate = function (t) {
return o[t.axis][t.name] || new i(t) }, n.Group = i }(), function () {
"use strict"; function t(t) {
this.$element = e(t) } var e = window.jQuery, i = window.Waypoint; e.each(["innerHeight", "innerWidth", "off", "offset", "on", "outerHeight", "outerWidth", "scrollLeft", "scrollTop"], function (e, i) {
t.prototype[i] = function () {
var t = Array.prototype.slice.call(arguments); return this.$element[i].apply(this.$element, t) } }), e.each(["extend", "inArray", "isEmptyObject"], function (i, o) {
t[o] = e[o] }), i.adapters.push({
name: "jquery", Adapter: t }), i.Adapter = t }(), function () {
"use strict"; function t(t) {
return function () {
var i = [], o = arguments[0]; return t.isFunction(arguments[0]) && (o = t.extend({
}, arguments[1]), o.handler = arguments[0]), this.each(function () {
var n = t.extend({
}, o, {
element: this }); "string" == typeof n.context && (n.context = t(this).closest(n.context)[0]), i.push(new e(n)) }), i } } var e = window.Waypoint; window.jQuery && (window.jQuery.fn.waypoint = t(window.jQuery)), window.Zepto && (window.Zepto.fn.waypoint = t(window.Zepto)) }();
script>
<header class="site-header">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<div class="wrapper">
<h1 class="site-header__title">HTPP服务器练习h1>
div>
<div style="word-break: break-all; width: 90%; text-align: left; margin: 0 auto;">这是一个用TCP协议实现的简单的HTTP协议的服务器,供学习的人理解HTTP的本质。该程序包装了一个HttpdServer类,HttpdServer的StartServer方法将在指定端口侦听文本消息,按HTTP协议处理和相应消息,从而实现HTTP服务器。StartServer之后将开启一个主进程侦听Web服务端口,该端口收到TCP连接时候创建DealClientRequest类开启子线程处理TCP请求,子线程侦听TCP内容,读取到浏览器提交的内容后构造抽取请求类型是GET还是POST,提取请求相对路径和参数,提取头信息等Request对象,如果是请求资源就响应资源,如果是请求调用就执行调用。div>
<div style="word-break: break-all; width: 90%; height:30px;text-align: left; margin: 0 auto;"> div>
<div style="word-break: break-all; width: 90%; text-align: left; margin: 0 auto;">
<div id="divInfo">div>
br>
<img src="linux.jpg" style="width:34px;height:34px;" />
<img src="windows.ico" style="width:34px;height:34px;" />
<div id="divAjax1">div>
br>
<div id="divAjax2">div>
br>
div>
header>
body>
html>
老铁们,努力吧