最近因工作需要为外部公司提供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服务实现的更多相关文章

  1. 通过HttpListener实现简单的Http服务

    使用HttpListener实现简单的Http服务 HttpListener提供一个简单的.可通过编程方式控制的 HTTP 协议侦听器.使用它可以很容易的提供一些Http服务,而无需启动IIS这类大型 ...

  2. 树莓派(Raspberry Pi)搭建简单的lamp服务

    树莓派(Raspberry Pi)搭建简单的lamp服务: 1. LAMP 的安装 sudo apt-get install apache2 mysql-server mysql-client php ...

  3. 构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介

    构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介 熟悉将用于 Apache Tuscany SCA for C++ 的 API.您将通过本文了解该 API 的主要组 ...

  4. Netty4.0学习笔记系列之三:构建简单的http服务(转)

    http://blog.csdn.net/u013252773/article/details/21254257 本文主要介绍如何通过Netty构建一个简单的http服务. 想要实现的目的是: 1.C ...

  5. 简单ESB的服务架构

    简单ESB的服务架构 这几个月一直在修改架构,所以迟迟没有更新博客. 新的架构是一个基于简单esb的服务架构,主要构成是esb服务注册,wcf服务,MVC项目构成. 首先,我门来看一看解决方案, 1. ...

  6. 新项目架构从零开始(三)------基于简单ESB的服务架构

    这几个月一直在修改架构,所以迟迟没有更新博客. 新的架构是一个基于简单esb的服务架构,主要构成是esb服务注册,wcf服务,MVC项目构成. 首先,我门来看一看解决方案, 1.Common 在Com ...

  7. 简单 TCP/IP 服务功能

    本主题使用每台 Windows 计算机上提供的 Echo 和 Quote of the Day 服务.在所有 Windows 版本中都提供了简单 TCP/IP 服务功能.该功能会提供了以下服务:Cha ...

  8. 使用Topshelf组件构建简单的Windows服务

    很多时候都在讨论是否需要了解一个组件或者一个语言的底层原理这个问题,其实我个人觉得,对于这个问题,每个人都有自己的看法,个人情况不同,选择的方式也就会不同了.我个人觉得无论学习什么,都应该尝试着去了解 ...

  9. 使用Axis2创建一个简单的WebService服务

    使用过Java进行过WebService开发都会听过或者接触过Apache Axis2,Axis2框架是应用最广泛的WebService框架之一了. 这里使用Axis2来开发和部署一个最简单的WebS ...

  10. [WCF REST] 一个简单的REST服务实例

    Get:http://www.cnblogs.com/artech/archive/2012/02/04/wcf-rest-sample.html [01] 一个简单的REST服务实例 [02] We ...

随机推荐

  1. java开发工具idea,在install时候报错The packaging for this project did not assign a file to the build artifact

    intellij中install报错:The packaging for this project did not assign a file to the build artifact 原因是run ...

  2. vim编辑16进制

    你可以在vim中可以把文件转换为16进制来显示: :%!xxd 解释:把所有的行(%)用本地(!)的xxd程序打开. xxd本是linux下一个显示.编辑.转换二进制的命令. 返回正常显示: :%!x ...

  3. Doxygen简介

    (转自:http://www.cnblogs.com/liuliunumberone/archive/2012/04/10/2441391.html) 一.什么是Doxygen? Doxygen 是一 ...

  4. python基础之模块二

    六 logging模块 6.1 函数式简单配置 import logging #导入模块 logging.debug('debug message') #调试消息 logging.debug('inf ...

  5. macOS 下 Visual Studio Code(VSCODE)安装配置及应用

    Visual Studio Code 重新定义了 Code 编辑. 在任何操作系统上编辑和调试应用程序内置 Git 支持1000 种以上的扩展免费和开源 为什么使用VSCODE? 我们来看看以下功能: ...

  6. Re-install Flyme or Native Google Android on Meizu MX4 Ubuntu (by quqi99)

    作者:张华 发表于:2017-06-23 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 ( http://blog.csdn.net/quqi99 ) ## ...

  7. 第2章 开始Flex

    * Flex开发中可用两种语言 1.MXML 2.ActionScript * Flex中使用两个组件集 1.MX (mx.*) 早期的Flex版本用到的组件集 2.Spark (spark.*) F ...

  8. HihoCoder 1183 : 连通性一·割边与割点(模板)

    连通性一·割边与割点 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失.为了避免再次 ...

  9. 选择第n大的数(分治法和排列实现)

    个人心得:在买的书上看到的一个经典分治题,题目意思就是给定一个数组,求第k小的数. 第一反应就是排序,然后返回第k-1位置的数就可以了,这样算法的复杂度是nlongn,在快速排序的基础下还是挺不错的. ...

  10. iOS 10 隐私权限设置

    iOS 10 开始对隐私权限更加严格,如果你不设置就会直接崩溃,现在很多遇到崩溃问题了,一般解决办法都是在info.plist文件添加对应的Key-Value就可以了. 以上Value值,圈出的红线部 ...