最近因工作需要为外部公司提供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. 18-THREE.JS 基本材质

    <!DOCTYPE html> <html> <head> <title></title> <script src="htt ...

  2. shell编程实例2

    1.vim  read_PERSON.sh 2. #!/bin/bash echo "What is your name?" read PERSON echo "Hell ...

  3. New Concept English three (37)

    28 words/minute 44 typing errors We have learnt to expect that trains will be punctual. After years ...

  4. mysql int(3)与int(11)的区别

    总结,int(M) zerofill,加上zerofill后M才表现出有点点效果,比如 int(3) zerofill,你插入到数据库里的是10,则实际插入为010,也就是在前面补充加了一个0.如果i ...

  5. Flask 通关攻略大全

    基本使用 from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello ...

  6. Java加载jar文件并调用jar文件当中有参数和返回值的方法

    在工作当中经常遇到反编译后的jar文件,并要传入参数了解其中的某些方法的输出,想到Java里面的反射可以实现加载jar文件并调用其中的方法来达到自己的目的.就写了个Demo代码. 以下的类可以编译生成 ...

  7. 【个人吐槽】C、Delphi、C#、java 摘抄

    作为个人的一个感受就是,在win平台上开发软件,别再他妈的用MFC了,不适合新手,上手太难.你妹,实现一个半透明的功能,一堆代码,而C#就他妈的几行话.靠. 似乎很多人都觉得Delphi已经没落了.过 ...

  8. 【ACM非算法部分】读入优化

    今天做了ACdream的比赛才知道原来还有读入优化这一说.Orz 读入一个整数的时候这么写: int a; scanf("%d",&a); 和 int a; char c; ...

  9. Java关闭线程的安全方法

    Java之前有一个api方法可以直接关闭线程,stop(),由于这个方法是强制性地关闭线程,有的时候会发生错误,之后就取消了,现在可用的方法主要有两种: 1.  在线程中加入一个成员变量,当一个fla ...

  10. UIImage+PYJColorBecomeImage

    UIImage+PYJColorBecomeImage.h: // // UIImage+PYJColorBecomeImage.h // 颜色转成图片 // // Created by PengYu ...