zoukankan      html  css  js  c++  java
  • 简单的HTTP服务实现

    最近因工作需要为外部公司提供http服务,由于内容比较少,同时为了方便安装,就想着自己写一个简单的服务器。

    思路是将一个Http服务器嵌入一个Windows Service程序中,于是在网上找了很多资料和源码。C#中实现http服务一般使用Socket或TcpListener两种,本文使用的是Socket方式。

    一、HTTP报文格式

    Post报文:

    POST /Adapter.KeyInitialization HTTP/1.1
    Content-Length: 191
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Host: 115.156.249.117:11250
    Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.5.1 (Java/1.7.0_45)
    Accept-Encoding: gzip,deflate

    postdata=%7B%22key_extra_info%22%3A%22zehge%22%2C%22key_name%22%3A%22%E9%92%A5%E5%8C%9935313032-5956-4331-3031-3039ffffffff%22%2C%22key_rfid%22%3A%2235313032-5956-4331-3031-3039ffffffff%22%7D

    熟悉编码的童鞋会发现,上面postdata=之后的部分其实是Url编码,所以在解析时需要用到HttpUtility.UrlDecode(String urlEncodeStr);

    【注意】post有两种传参数的方式

    1、参数放在url中,Content-Length值为0。

    请求报文:

    POST http://192.168.80.194:8080/JBPlatform/DFESerUpLoadData.posttest?jsonData=tetet HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    cache-control: no-cache
    Postman-Token: 202cd59d-b2e2-4370-a88c-8e7abaf7a8d9
    User-Agent: PostmanRuntime/7.6.0
    Accept: */*
    Host: 192.168.80.194:8080
    accept-encoding: gzip, deflate
    content-length: 0
    Connection: keep-alive

    返回报文:

    HTTP/1.1 200
    Content-Type: text/plain;charset=UTF-8
    Content-Length: 64
    Date: Thu, 23 Apr 2020 03:46:50 GMT
    
    {"data":"成功","jsonData":"tetet","result":1,"runStatus":null}

    2、参数放在body中,Content-Length需根据body计算长度

    请求报文:

    POST http://192.168.80.194:8080/JBPlatform/DFESerUpLoadData.posttest HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    cache-control: no-cache
    Postman-Token: be9877fa-bad6-49db-82cf-5a993faf2fc8
    User-Agent: PostmanRuntime/7.6.0
    Accept: */*
    Host: 192.168.80.194:8080
    accept-encoding: gzip, deflate
    content-length: 15
    Connection: keep-alive
    
    jsonData=tetete

    返回报文:

    HTTP/1.1 200
    Content-Type: text/plain;charset=UTF-8
    Content-Length: 65
    Date: Thu, 23 Apr 2020 03:31:36 GMT
    
    {"data":"成功","jsonData":"tetete","result":1,"runStatus":null}

    Get报文:

    GET /  HTTP/1.1
    Content-Length: 191
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Host: 115.156.249.117:11250
    Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.5.1 (Java/1.7.0_45)
    Accept-Encoding: gzip,deflate

    二、简单HTTP服务器的实现

    1、根据报文格式写一个解析报文的类

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    /// <summary>
    /// 描述:HTTP请求类,用于解析HTTP报文
    /// 编写:gc
    /// 日期:2016/3/8
    /// 版本:1.0.1.3
    /// </summary>
    public class MyHTTPRequest
    {
        public string Method { get; private set; }
        public string Url { get; private set; }
        public string Protocol { get; private set; }
        private Dictionary<string, string> properties = null;
    
        public Dictionary<string, string> Properties
        {
            get
            {
                if (properties == null)
                    properties = new Dictionary<string, string>();
                return properties;
            }
        }
    
        public string Parameter { get; private set; }
    
        public MyHTTPRequest()
        {
            //Nothing to do!
        }
    
        public MyHTTPRequest(string requestString)
        {
            using (StringReader sr = new StringReader(requestString))
            {
                var line = sr.ReadLine();
                if (!IsNullOrWhiteSpace(line))
                {
                    string[] headerInfo = line.Split(new char[] { ' ' });
                    if (headerInfo.Length == 3)
                    {
                        SetHeader(headerInfo[0], headerInfo[1], headerInfo[2]);
                        for (line = sr.ReadLine(); !IsNullOrWhiteSpace(line); line = sr.ReadLine())
                        {
                            string[] tokens = line.Split(new string[] { ": " }, StringSplitOptions.None);
                            if (tokens.Length == 2)
                            {
                                Properties.Add(tokens[0], tokens[1]);
                            }
                            else
                            {
                                //Console.WriteLine(line); //打印调试信息
                            }
                        }
    
                        switch (this.Method.ToUpper())
                        {
                            case "GET":
                                Parameter = string.Empty;
                                break;
                            case "POST":
                                {
                                    try
                                    {
                                        string tempStr = sr.ReadToEnd().ToString().Trim();
    
                                        SKMPInput input = new SKMPInput(tempStr);
                                        Parameter = input.InputParameter;
                                        //SKMPInput input = JSONConvertHelper.ToObject<SKMPInput>(tempStr);
                                        //Parameter = JSONConvertHelper.ToJSONString(input.postdata);
                                    }
                                    catch (Exception ex)
                                    {
                                        string tempStr = sr.ReadToEnd().ToString().Trim();
                                        SKMPInput input = new SKMPInput(tempStr);
                                        Parameter = input.InputParameter;
    
                                        //SKMPInput input = JSONConvertHelper.ToObject<SKMPInput>(tempStr);
                                        //Parameter = JSONConvertHelper.ToJSONString(input.postdata);
                                    }
    
                                    //Parameter = sr.ReadToEnd().ToString().Trim();
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }
    
        public void SetHeader(string method, string url, string protocol)
        {
            this.Method = method;
            this.Url = url;
            this.Protocol = protocol;
        }
    
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Method:{0}<br/>", Method);
            sb.AppendFormat("Url:{0}<br/>", Url);
            sb.AppendFormat("Protocol:{0}<br/>", Protocol);
            foreach (var item in Properties)
            {
                sb.AppendFormat("{0}:{1}<br/>", item.Key, item.Value);
            }
            sb.AppendFormat("Parameter:{0}<br/>", Parameter);
            return sb.ToString();
        }
        public static bool IsNullOrWhiteSpace(string value)
        {
            if (value != null)
            {
                for (int i = 0; i < value.Length; i++)
                {
                    if (!char.IsWhiteSpace(value[i]))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }

    2、服务器主体程序

    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    /// <summary>
    /// 描述:HTTP服务器
    /// 编写:gc
    /// 日期:2016/3/8
    /// 版本:1.0.1.3
    /// </summary>
    public class MyHTTPServer
    {
        private string companyName = "**公司";
        private string localServerInfo = "MyHTTPServer/1.0";
        private string localServerIP = "localhost";
        private int localServerPort = 8080;
        private string localServerContentType = "image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
        private Encoding localServerEncoding = Encoding.GetEncoding("gb2312");
        private TicketService ticketService = null;
    
        private string targetHostIP;
        private int targetHostPort = 11250;
    
        private Thread m_Thread; //HTTP服务主线程
        public bool IsAlive
        {
            get
            {
                return this.m_Thread.IsAlive;
            }
        }
        /// <summary>
        /// 监听标志位
        /// </summary>
        private bool isRun = false;
        Socket serverSocket;
        public CookieContainer CookieContainer { get; set; }
    
        public MyHTTPServer()
        {
            Initialize();
        }
    
        /// <summary>
        /// 初始化服务器参数
        /// </summary>
        private void Initialize()
        {
            this.companyName = ServerGlobal.ServerGlobalInstance.CompanyName;
            this.localServerInfo = ServerGlobal.ServerGlobalInstance.LocalServerInfo;
            this.localServerIP = ServerGlobal.ServerGlobalInstance.LocalServerIP;
            this.localServerPort = ServerGlobal.ServerGlobalInstance.LocalServerPort;
            this.localServerContentType = ServerGlobal.ServerGlobalInstance.LocalServerContentType;
            this.localServerEncoding = ServerGlobal.ServerGlobalInstance.LocalServerEncoding;
            this.targetHostIP = ServerGlobal.ServerGlobalInstance.TargetHostIP;
            this.targetHostPort = ServerGlobal.ServerGlobalInstance.TargetHostPort;
    
            ticketService = TicketService.TicketServiceInstance;
        }
    
        /// <summary>
        /// 启动服务器
        /// </summary>
        public void Start()
        {
            isRun = true;
            this.m_Thread = new Thread(new ThreadStart(this.Listen));
            this.m_Thread.Start();
    
            ticketService.Start();
        }
    
        /// <summary>
        /// 停止服务器
        /// </summary>
        public void Stop()
        {
            isRun = false;
            this.m_Thread.Abort();
            serverSocket.Close();//释放Socket占用的端口号
    
            ticketService.Stop();
        }
    
        /// <summary>
        /// 监听
        /// </summary>
        private void Listen()
        {
            try
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Any, this.localServerPort);
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.Bind(ipep);
                serverSocket.Listen(10);
                while (isRun)
                {
                    Socket clientSocket = serverSocket.Accept();
                    ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveData), clientSocket);
                }
            }
            catch (Exception ex)
            {
                MyServerRunLog.WriteRunLog("listening Error: " + ex.Message);
            }
        }
    
        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="socket">Socket</param>
        private void ReceiveData(object socket)
        {
            Socket clientSocket = socket as Socket;
            Byte[] buffer = new Byte[8192]; //Socket默认缓冲区为8K,当数据量大于8K时此处可能会产生Bug
            IPEndPoint clientep = (IPEndPoint)clientSocket.RemoteEndPoint;
            string clientMessage = string.Empty;
    
            try
            {
                clientSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                clientMessage = localServerEncoding.GetString(buffer);
    
                if (IsNullOrWhiteSpace(clientMessage))
                {
                }
                byte[] data = WriteSuccessResponse(clientMessage);
                clientSocket.Send(data, data.Length, SocketFlags.None);
    
            }
            catch (Exception ex)
            {
                StringBuilder sb = new StringBuilder();
                WriteFailure(sb);
                sb.AppendFormat("<html><head><title>Server throw an exception:{1}</title></head><body>{0}</body></html>", ex.ToString(), ex.GetType().FullName);
                var data = localServerEncoding.GetBytes(sb.ToString());
                clientSocket.Send(data, data.Length, SocketFlags.None);
            }
            clientSocket.Shutdown(SocketShutdown.Both);
        }
    
        private byte[] WriteSuccessResponse(string clientMessage)
        {
            MyHTTPRequest request = new MyHTTPRequest(clientMessage);
            StringBuilder sb = new StringBuilder();
    
            sb.AppendFormat("{0} 200 ok
    ", request.Protocol);
    
            if (null != request.Method)
            {
                switch (request.Method.ToUpper())
                {
                    case "GET":
                        {
                            sb.AppendLine("connection: close");
                            sb.AppendLine();
                            sb.AppendFormat("<html><head><title>Connect Sucess {1}</title></head><body><h2>Welcome to my http server</h2>{0}<h3><div><h3>Your requested content:</h3>{2}</div>Author: <a href='http://www.baidu.com/'>百度</a></h3></body></html>", request.ToString(), request.Url, clientMessage);
                            return Encoding.GetEncoding("gb2312").GetBytes(sb.ToString()); //
                        }
                        break;
                    case "POST":
                        {
                            string returnValue = DealWithUrl(request);
                            StringBuilder contentBody = new StringBuilder();
                            contentBody = contentBody.Append(returnValue);
                            //contentBody = contentBody.AppendFormat("<html><head><title>You request Url is {0}</title></head><body>{1}</body></html>", request.Url, returnValue);
                            int contentLength = localServerEncoding.GetBytes(contentBody.ToString()).Length;
    
                            sb.AppendLine(string.Format("Server: {0} ", localServerInfo));
                            sb.AppendLine(string.Format("Date: {0} ", DateTime.Now.GetDateTimeFormats('r')[0].ToString()));
                            sb.AppendLine(string.Format("Content-Type: {0} ", localServerContentType));
                            sb.AppendLine(string.Format("Last-Modified: {0} ", DateTime.Now.GetDateTimeFormats('r')[0].ToString()));
                            sb.AppendLine(string.Format("Content-Length: {0} ", contentLength));
    
                            sb.AppendLine();
                            sb.AppendLine(contentBody.ToString());
                        }
                        break;
                    default:
                        sb.AppendFormat("<html><head><title>You request is {0}</title></head><body><h2>Welcome to my http server</h2>{1}<h3>Author: <a href='mailto:http://www.baidu.com/'>百度</a></h3></body></html>", request.Url, request.ToString());
                        break;
                }
            }
    
            return localServerEncoding.GetBytes(sb.ToString());
    
        }
    
        /// <summary>
        /// 根据Url调用对应的接口方法处理数据
        /// </summary>
        /// <param name="request"></param>
        /// <returns>JSON格式字符串</returns>
        private string DealWithUrl(MyHTTPRequest request)
        {
            string returnValue = "未找到对应的接口方法:" + request.Url;
            switch (request.Url)//.ToUpper()) 
            {
                case "/Local.GetInfo":
                    returnValue = GetInfo();
    
                case "/Local.WorkWithInput":
                    returnValue = WorkWithInput(request.Parameter);
                    break;
    
    
                    break;
                default:
                    break;
            }
            return returnValue;
        }
    
        #region Local.xxx 表示提供给外部系统调用的接口
        /// <summary>
        /// 1)获取**信息
        /// </summary>
        /// <returns></returns>
        public string GetInfo()
        {
    
        }
    
    
        #endregion
    
        public void WriteFailure(StringBuilder sw)
        {
            sw.AppendLine("http/1.0 404 file not found");
            sw.AppendLine("connection: close");
            sw.AppendLine();
        }
    
        #region Target.xxxx 表示本地系统调用外部系统的接口
    
        private string UploadInfo(UploadInput input)
        {
    
        }
    
    
        #endregion
        private string Post(string postUrl, string paramData)
        {
            string result = string.Empty;
            Stream stream = null;
            StreamReader sr = null;
            HttpWebResponse response = null;
            try
            {
                byte[] byteArray = Encoding.UTF8.GetBytes(paramData);
                HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));
                webReq.Method = "POST";
                webReq.ContentType = localServerContentType;
                //webReq.ContentType = "application/x-www-form-urlencoded";
                webReq.Accept = "text/json";
                webReq.UserAgent = localServerInfo;
                webReq.ContentLength = byteArray.Length;
                webReq.ServicePoint.Expect100Continue = false;
                webReq.CookieContainer = CookieContainer;
                stream = webReq.GetRequestStream();
                stream.Write(byteArray, 0, byteArray.Length);
                response = (HttpWebResponse)webReq.GetResponse();
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    response.Cookies = CookieContainer.GetCookies(webReq.RequestUri);
                    sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                    result = sr.ReadToEnd();
                }
                else
                {
                    Debug.WriteLine(response.StatusCode);
                }
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                }
                if (response != null)
                {
                    response.Close();
                }
                if (stream != null)
                {
                    stream.Close();
                }
            }
            return result;
        }
    
        public static bool IsNullOrWhiteSpace(string value)
        {
            if (value != null)
            {
                for (int i = 0; i < value.Length; i++)
                {
                    if (!char.IsWhiteSpace(value[i]))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    
        #region JSONObject转换
        public static string ToJSON(object value)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(value, Formatting.Indented, new JsonSerializerSettings
            {
                ContractResolver = new LowerCasePropertyNamesContractResolver(),
                DateFormatString = "yyyy-MM-dd HH:mm:ss.fff",
                ReferenceLoopHandling = ReferenceLoopHandling.Serialize
            });
        }
    
        private class LowerCasePropertyNamesContractResolver : DefaultContractResolver
        {
            public LowerCasePropertyNamesContractResolver()
            : base(true)
            {
            }
    
            protected override string ResolvePropertyName(string propertyName)
            {
                return propertyName.ToLower();
            }
        }
    
        public static T ToObject<T>(string json)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
            //return fastJSON.JSON.ToObject<T>(json);
        }
        #endregion
    
    
    }



    基于Socket的服务器程序基本完成。

  • 相关阅读:
    QT QT程序初练
    Linux Shell编程三
    Linux Shell编程二
    git操作
    git push命令
    Zabbix全方位告警接入-电话/微信/短信都支持
    CentOS 7安装TigerVNC Server
    MySQL各版本的区别
    MariaDB Galera Cluster 部署
    MySQL高可用方案MHA的部署和原理
  • 原文地址:https://www.cnblogs.com/cheng2015/p/5197899.html
Copyright © 2011-2022 走看看