博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BeetleX之简单HTTP/HTTPS实现
阅读量:4033 次
发布时间:2019-05-24

本文共 6706 字,大约阅读时间需要 22 分钟。

        在通讯应用中很多时候需要和已有标准的应用协议进行通讯,针对这情况就要针对相应协议的实现;标准协议上考虑的情况比较多,所以协议的复杂度也相对高些,对比之前的Protobuf通讯的简单协议来说则会复杂。接下来用组件去实现一个简单的HTTP协议服务,让浏览器可以去访问它。

HTTP协议

        对于HTTP协议的介绍相信也不用过多描述,毕竟这个协议已经应用了N年了,网上针对这一协议的介绍也非常多。这协议的版本有1.0,1.1和2.0,接下来实现的是HTTP1.1。其实更符合多场景应用是2.0,不过2.0的协议复杂度就比较高了,所以就不在这里实现介绍了。

        HTTP 1.1协议只允许同一时间处理一个请求,就是当服务端接收到请求后直到响应完成才会处理下一下请求。为了满足这需要针对通讯协议制定了Request和Response对象。

Request对象

        该对象主要用于收集HTTP的请求信息,定义如下:

class HttpRequest    {        //当前HTTP版本信息        public string HttpVersion { get; set; }        //请求的方法        public string Method { get; set; }        //基础的url        public string BaseUrl { get; set; }        //客户端IP        public string ClientIP { get; set; }        //请求路径        public string Path { get; set; }        //Url参数        public string QueryString { get; set; }        //完整URL        public string Url { get; set; }        //头部信息        public Dictionary
Headers { get; private set; } = new Dictionary
();        //HTTP内容 public byte[] Body { get; set; } //HTTP内容长度 public int ContentLength { get; set; }        //请求对象状态 public RequestStatus Status { get; set; } = RequestStatus.None; }

以上是一个HTTP请求的简单描述对象,服务会根据网络数据根据HTTP协议转换成相应的对象消息。

HttpResponse对象

        该对象用于设置请求响应内容,定义如下:

class HttpResponse : IWriteHandler    {        public HttpResponse()        {            Headers["Content-Type"] = "text/html";        }        public string HttpVersion { get; set; } = "HTTP/1.1";        public int Status { get; set; }        public string StatusMessage { get; set; } = "OK";        public string Body { get; set; }        public Dictionary
Headers = new Dictionary
(); public void Write(Stream stream) { var pipeStream = stream.ToPipeStream();            //写入响应状态 pipeStream.WriteLine($"{HttpVersion} {Status} {StatusMessage}");            //写入头部信息 foreach (var item in Headers) pipeStream.WriteLine($"{item.Key}: {item.Value}"); byte[] bodyData = null; if (!string.IsNullOrEmpty(Body)) { bodyData = Encoding.UTF8.GetBytes(Body); } if (bodyData != null) { pipeStream.WriteLine($"Content-Length: {bodyData.Length}"); } pipeStream.WriteLine("");            //写入响应消息体 if (bodyData != null) { pipeStream.Write(bodyData, 0, bodyData.Length); } Completed?.Invoke(this); } public Action
Completed { get; set; } }

对象实现了IWriteHandler接口,用于告诉组件提供自定义流输出实现。

协议实现

        在这个示例中协议分析并没有实现IPacket,而是直接接管SessionReceive方法来对流进行HTTP协议分析,具体实现代码如下:

public override void SessionReceive(IServer server, SessionReceiveEventArgs e){    var request = GetRequest(e.Session);    var pipeStream = e.Stream.ToPipeStream();    if (LoadRequest(request, pipeStream) == RequestStatus.Completed)    {        OnCompleted(request, e.Session);    }}private RequestStatus LoadRequest(HttpRequest request, PipeStream stream){    //分析HTTP请求信息    LoadRequestLine(request, stream);    //分析头信息    LoadRequestHeader(request, stream);    //加载Body    LoadRequestBody(request, stream);    return request.Status;}private void LoadRequestLine(HttpRequest request, PipeStream stream){    if (request.Status == RequestStatus.None)    {        if (stream.TryReadLine(out string line))        {            var subItem = line.SubLeftWith(' ', out string value);            request.Method = value;            subItem = subItem.SubLeftWith(' ', out value);            request.Url = value;            request.HttpVersion = subItem;            subItem = request.Url.SubRightWith('?', out value);            request.QueryString = value;            request.BaseUrl = subItem;            request.Path = subItem.SubRightWith('/', out value);            if (request.Path != "/")                request.Path += "/";            request.Status = RequestStatus.LoadingHeader;        }    }}private void LoadRequestHeader(HttpRequest request, PipeStream stream){    if (request.Status == RequestStatus.LoadingHeader)    {        while (stream.TryReadLine(out string line))        {            if (string.IsNullOrEmpty(line))            {                if (request.ContentLength == 0)                {                    request.Status = RequestStatus.Completed;                }                else                {                    request.Status = RequestStatus.LoadingBody;                }                return;            }            var name = line.SubRightWith(':', out string value);            if (String.Compare(name, "Content-Length", true) == 0)            {                request.ContentLength = int.Parse(value);            }            request.Headers[name] = value.Trim();        }    }}private void LoadRequestBody(HttpRequest request, PipeStream stream){    if (request.Status == RequestStatus.LoadingBody)    {        if (stream.Length >= request.ContentLength)        {            var data = new byte[request.ContentLength]; ;            stream.Read(data, 0, data.Length);            request.Body = data;            request.Status = RequestStatus.Completed;        }    }}

在分析过程中最常用的方法是TryReadLine,主要原因HTTP的头信息数据都是以换行的方式来描述,直到读取一个空行表明头部已结。如果存在Content-Length头信息描述说明存在消息体(HTTP有两种描述消息体的情况,这里就不多作介绍了)。

服务处理

        协议处理好后就可以集成在服务中,相对于协议分析来说集成就更简单了。

public static void Main(string[] args){    mServer = SocketFactory.CreateTcpServer
(); mServer.Options.DefaultListen.Port = 80; mServer.Options.AddListenSSL("ssl.pfx", "123456"); mServer.Open(); System.Threading.Thread.Sleep(-1);}private void OnCompleted(HttpRequest request, ISession session){ HttpResponse response = new HttpResponse(); StringBuilder sb = new StringBuilder(); sb.AppendLine(""); sb.AppendLine(""); sb.AppendLine($"

Method:{request.Method}

"); sb.AppendLine($"

Url:{request.Url}

"); sb.AppendLine($"

Path:{request.Path}

"); sb.AppendLine($"

QueryString:{request.QueryString}

"); sb.AppendLine($"

ClientIP:{request.ClientIP}

"); sb.AppendLine($"

Content-Length:{request.ContentLength}

"); foreach (var item in request.Headers) { sb.AppendLine($"

{item.Key}:{item.Value}

"); } sb.AppendLine(""); sb.AppendLine(""); response.Body = sb.ToString(); ClearRequest(session); session.Send(response);}

在服务中把默认监听的端口改成80,然后添加一个SSL监听用于支持HTTPS访问;示例中通过OnCompleted方法响应请求内容,主要返回的内容是把当前请求的请求详细信息输出。

访问结果

        启动服务后可以通过浏览器访问相关地址:

  • HTTP

  • HTTPS

由于证书是自己创建的,所以会被浏览器标记为不安全。

下载

链接:https://pan.baidu.com/s/1P7QTSjJH4Q1ftHiRoQT8MQ 

提取码:7kig 

【BeetleX通讯框架代码详解】BeetleX

开源跨平台通讯框架(支持TLS)

轻松实现高性能:tcp、http、websocket、redis、rpc和网关等服务应用

https://beetlex.io

转载地址:http://abkdi.baihongyu.com/

你可能感兴趣的文章
Java的对象驻留
查看>>
自己动手写GC
查看>>
Java 8新特性终极指南
查看>>
logback高级特性使用(二) 自定义Pattern模板
查看>>
JVM并发机制探讨—内存模型、内存可见性和指令重排序
查看>>
可扩展、高可用服务网络设计方案
查看>>
如何构建高扩展性网站
查看>>
微服务架构的设计模式
查看>>
持续可用与CAP理论 – 一个系统开发者的观点
查看>>
nginx+tomcat+memcached (msm)实现 session同步复制
查看>>
c++字符数组和字符指针区别以及str***函数
查看>>
c++类的操作符重载注意事项
查看>>
c++模板与泛型编程
查看>>
STL::deque以及由其实现的queue和stack
查看>>
WAV文件解析
查看>>
DAC输出音乐2-解决pu pu 声
查看>>
WPF中PATH使用AI导出SVG的方法
查看>>
WPF UI&控件免费开源库
查看>>
QT打开项目提示no valid settings file could be found
查看>>
Win10+VS+ESP32环境搭建
查看>>