完整项目托管地址:https://github.com/hooow-does-it-work/http
接前篇内容:C#实现HTTP服务器:(2)向客户端发送Hello World
为了方便后续处理各种HTTP响应,封装一个简单的应答器,作为各种响应类型的父类。
应答器内部会自动生成HttpResponse,并暴露出HttpResponse和一些常用的接口。
不同写入数据的方法,最终都是调用的Write(Stream stream, byte[] buffer, int offset, int size)
这个重载。
下篇文章将使用应答器,使用Chunked模式向客户端发送数据。
0、使用应答器向客户端写入响应
public class HttpServer : TcpIocpServer
{
protected override void NewClient(Socket client)
{
Stream stream = new NetworkStream(client, true);
//捕获一个HttpRequest
HttpRequest request = HttpRequest.Capture(stream);
//需要发送到客户端的内容
string responseText = $"<p>hello world!</p><pre>{request.GetAllRequestHeaders()}</pre>";
//内容对应的二进制数据
byte[] responseBody = Encoding.ASCII.GetBytes(responseText);
//实例化一个应答器
HttpResponser responser = new HttpResponser();
//设置响应内容长度和类型,应答器对常用的标头作了快捷设置
responser.ContentLength = responseBody.Length;
responser.ContentType = "text/html";
responser.KeepAlive = false;
//发送响应内容
responser.Write(stream, responseBody);
responser.End(stream);
stream.Close();
}
}
1、应答器HttpResponser源码
/// <summary>
/// HTTP应答器,作为各种不同响应资源的父类
/// </summary>
public class HttpResponser
{
private HttpResponse _response = null;
private bool _headerWritten = false;
public HttpResponse Response => _response;
public HttpResponser() : this(200) { }
public HttpResponser(int statusCode)
{
_response = new HttpResponse(statusCode);
///设定一些基本标头
///null值的标头不会被写入客户端
///提前将标头设置为null,可以确保标头写入客户端的顺序会按照设置的顺序来
_response.Headers["Cache-Control"] = null;
_response.Headers["Pragma"] = null;
_response.Headers["Content-Type"] = null;
_response.Headers["Expires"] = null;
_response.Headers["Content-Type"] = null;
_response.Headers["Content-Length"] = null;
_response.Headers["Content-Encoding"] = null;
_response.Headers["Content-Range"] = null;
_response.Headers["Server"] = null;
_response.Headers["X-Powered-By"] = null;
_response.Headers["Location"] = null;
_response.Headers["Date"] = DateTime.UtcNow.ToString("r");
_response.Headers["Connection"] = null;
}
public string this[string name] {
get => _response.Headers[name];
set => _response.Headers[name] = value;
}
public int ContentLength
{
set => _response.Headers["Content-Length"] = value.ToString();
}
public string ContentType {
get => _response.Headers["Content-Type"];
set => _response.Headers["Content-Type"] = value;
}
public bool KeepAlive {
get => _response.Headers["Connection"] != "close";
set => _response.Headers["Connection"] = value ? "keep-alive" : "close";
}
/// <summary>
/// 向客户端写入响应头
/// </summary>
/// <param name="stream">要写入的数据流</param>
protected virtual void WriteHeader(Stream stream)
{
if (_headerWritten) return;
_headerWritten = true;
string responseHeaders = _response.GetAllResponseHeaders();
byte[] responseHeaderBuffer = Encoding.ASCII.GetBytes(responseHeaders);
//发送响应头
stream.Write(responseHeaderBuffer, 0, responseHeaderBuffer.Length);
}
/// <summary>
/// 向基础流写入文本,默认编码为UTF8
/// </summary>
/// <param name="stream">基础流</param>
/// <param name="content">内容文本</param>
public void Write(Stream stream, string content)
{
Write(stream, content, Encoding.UTF8);
}
/// <summary>
/// 使用指定编码向基础流写入文本
/// </summary>
/// <param name="stream">基础流</param>
/// <param name="content">内容文本</param>
/// <param name="encoding">文本编码</param>
public void Write(Stream stream, string content, Encoding encoding)
{
byte[] buffer = encoding.GetBytes(content);
Write(stream, buffer, 0, buffer.Length);
}
/// <summary>
/// 向基础流写入缓冲内容
/// </summary>
/// <param name="stream">基础流</param>
/// <param name="buffer">缓冲内容</param>
public void Write(Stream stream, byte[] buffer)
{
Write(stream, buffer, 0, buffer.Length);
}
/// <summary>
/// 向基础流写入输入流
/// </summary>
/// <param name="stream">基础流</param>
/// <param name="input">输入流</param>
public void Write(Stream stream, Stream input)
{
byte[] buffer = new byte[65536];
int rec;
while ((rec = input.Read(buffer, 0, 65536)) > 0)
{
Write(stream, buffer, 0, rec);
}
}
/// <summary>
/// 向基础流写入缓冲内容
/// </summary>
/// <param name="stream">基础流</param>
/// <param name="buffer">缓冲内容</param>
/// <param name="offset">写入的数据在缓冲区的偏移</param>
/// <param name="size">写入大小</param>
public virtual void Write(Stream stream, byte[] buffer, int offset, int size)
{
WriteHeader(stream);
stream.Write(buffer, offset, size);
}
/// <summary>
/// 虚方法,可以确保响应头写到了浏览器
/// 子类可以在重载中执行一些自己的逻辑
/// 例如确保固定Content-Length的数据发送准确
/// 确保Chunked传输的结束包发送
/// </summary>
public virtual void End(Stream stream)
{
WriteHeader(stream);
}
/// <summary>
/// 打开流,用于写入
/// </summary>
/// <param name="baseStream">基础流</param>
/// <returns></returns>
public virtual Stream OpenWrite(Stream baseStream)
{
WriteHeader(baseStream);
return baseStream;
}
}