台达PLC解密台达PLC解密次数限度【业余】【信用【18230062509】在通信利用中很多时候须要和已有规范的利用协定进行通信,针对这状况就要针对相应协定的实现;标准协议上思考的状况比拟多,所以协定的复杂度也绝对高些,比照之前的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<string, string> Headers { get; private set; } = new Dictionary<string, string>();        //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<string, string> Headers = new Dictionary<string, string>();        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<IWriteHandler> 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<Program>();    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("<html>");    sb.AppendLine("<body>");    sb.AppendLine($"<p>Method:{request.Method}</p>");    sb.AppendLine($"<p>Url:{request.Url}</p>");    sb.AppendLine($"<p>Path:{request.Path}</p>");    sb.AppendLine($"<p>QueryString:{request.QueryString}</p>");    sb.AppendLine($"<p>ClientIP:{request.ClientIP}</p>");    sb.AppendLine($"<p>Content-Length:{request.ContentLength}</p>");    foreach (var item in request.Headers)    {        sb.AppendLine($"<p>{item.Key}:{item.Value}</p>");    }    sb.AppendLine("</body>");    sb.AppendLine("</html>");    response.Body = sb.ToString();    ClearRequest(session);    session.Send(response);}复制代码

在服务中把默认监听的端口改成80,而后增加一个SSL监听用于反对HTTPS拜访;示例中通过OnCompleted办法响应申请内容,次要返回的内容是把以后申请的申请详细信息输入。

拜访后果

启动服务后能够通过浏览器拜访相干地址:

  • HTTP

  • HTTPS

因为证书是本人创立的,所以会被浏览器标记为不平安。