简单的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);
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、根据报文格式写一个解析报文的类
/// <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、服务器主体程序
/// <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\r\n", 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的服务器程序基本完成。
简单的HTTP服务实现的更多相关文章
- 通过HttpListener实现简单的Http服务
使用HttpListener实现简单的Http服务 HttpListener提供一个简单的.可通过编程方式控制的 HTTP 协议侦听器.使用它可以很容易的提供一些Http服务,而无需启动IIS这类大型 ...
- 树莓派(Raspberry Pi)搭建简单的lamp服务
树莓派(Raspberry Pi)搭建简单的lamp服务: 1. LAMP 的安装 sudo apt-get install apache2 mysql-server mysql-client php ...
- 构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介
构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介 熟悉将用于 Apache Tuscany SCA for C++ 的 API.您将通过本文了解该 API 的主要组 ...
- Netty4.0学习笔记系列之三:构建简单的http服务(转)
http://blog.csdn.net/u013252773/article/details/21254257 本文主要介绍如何通过Netty构建一个简单的http服务. 想要实现的目的是: 1.C ...
- 简单ESB的服务架构
简单ESB的服务架构 这几个月一直在修改架构,所以迟迟没有更新博客. 新的架构是一个基于简单esb的服务架构,主要构成是esb服务注册,wcf服务,MVC项目构成. 首先,我门来看一看解决方案, 1. ...
- 新项目架构从零开始(三)------基于简单ESB的服务架构
这几个月一直在修改架构,所以迟迟没有更新博客. 新的架构是一个基于简单esb的服务架构,主要构成是esb服务注册,wcf服务,MVC项目构成. 首先,我门来看一看解决方案, 1.Common 在Com ...
- 简单 TCP/IP 服务功能
本主题使用每台 Windows 计算机上提供的 Echo 和 Quote of the Day 服务.在所有 Windows 版本中都提供了简单 TCP/IP 服务功能.该功能会提供了以下服务:Cha ...
- 使用Topshelf组件构建简单的Windows服务
很多时候都在讨论是否需要了解一个组件或者一个语言的底层原理这个问题,其实我个人觉得,对于这个问题,每个人都有自己的看法,个人情况不同,选择的方式也就会不同了.我个人觉得无论学习什么,都应该尝试着去了解 ...
- 使用Axis2创建一个简单的WebService服务
使用过Java进行过WebService开发都会听过或者接触过Apache Axis2,Axis2框架是应用最广泛的WebService框架之一了. 这里使用Axis2来开发和部署一个最简单的WebS ...
- [WCF REST] 一个简单的REST服务实例
Get:http://www.cnblogs.com/artech/archive/2012/02/04/wcf-rest-sample.html [01] 一个简单的REST服务实例 [02] We ...
随机推荐
- 五 web爬虫,scrapy模块,解决重复ur——自动递归url
一般抓取过的url不重复抓取,那么就需要记录url,判断当前URL如果在记录里说明已经抓取过了,如果不存在说明没抓取过 记录url可以是缓存,或者数据库,如果保存数据库按照以下方式: id URL加密 ...
- Node.js小白开路(一)-- console篇
在所有内容的学习之中我们经常首先要接受到的常常很大一部分为命令行或是工具的内容展示,console内容为node.js在命令行中答应数据内容的一个途径. Console是nodejs中的元老级模块了. ...
- 第七次scrum meeting记录
文章负责:张华杰 日期:2017年10月31日 会议地点:主楼主南201 各组员工作情况 团队成员 昨日完成任务 明日要完成任务 赵晓宇 课程列表页面搭建 issue20 课程列表页面搭建(part ...
- scrapy的简单使用以及相关设置属性的介绍
0. 楔子(一个最简单的案例) 1.scrapy.Spider scrapy.spiders.Spider name allowed_domains start_urls custom_setting ...
- Linux命令四
作业一: 1) 开启Linux系统前添加一块大小为20G的SCSI硬盘 2) 开启系统,右击桌面,打开终端 安装的是命令行界面 3) 为新加的硬盘分区,一个主分区大小为10G,剩余空间给扩展分区,在扩 ...
- hibernate validate验证框架中@NotEmpty、@NotbBank、@NotNull的区别
Hibernate Validator验证框架中@NotEmpty.@NotBlank.@NotNull 的区别 Hibernate Validator验证框架中@NotEmpty.@NotBlank ...
- RPi 3.5寸 电阻屏
/***************************************************************************** * RPi 3.5寸 电阻屏 * 说明: ...
- avalon 总线时序关系理解
对于读,等待时间指的是从端口捕获数据的时间相对于read信号的延时 建立时间指的是read信号相对于chipselect和addr的延时时间 对于写,等待时间指的是相对于非等待情况下各个信号的延时时间 ...
- ubuntu下访问支付宝官网,安装安全控件
(1)根据支付宝提示下载安装控件的压缩包 aliedit.tar.gz (2)解压安装 (3)重启浏览器就可以了
- PHP使用curl请求https站点的常见错误及解决方案
使用curl请求http站点和https站点最大的不同就是https站点有证书验证这一环节,如果证书验证不通过则无法发起请求,不管是请求什么类型的站点遇到问题时先把错误码打印出来看一下,打印错误码的代 ...