1.问题描述
做一个二维码摆渡机的发送端和接收端程序,用于网络物理隔离。摆渡机是第三方厂家的设备,该设备提供一个接口用于接收发送端传输的数据,摆渡机处理该数据并调用接收端的接口,将数据传递给接收端。
本文主要记录接收端程序如何使用 HttpListener
进行监听HTTP请求,并且解析POST请求中携带的参数。如何构造 content-type
为 multipart/form-data; boundary=
的POST请求,请参考:https://cylycgs.blog.csdn.net/article/details/101019468
下面是发送端和接收端的截图:
2.使用HttpListener监听
关于HttpListener的官方文档在这里:https://docs.microsoft.com/en-us/dotnet/api/system.net.httplistener?view=netframework-4.5
如下是关于如何使用 HttpListener
的相关代码,有详细的注释:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Windows.Forms;
namespace HttpListenerDemo
{
public partial class FrmResponse : Form
{
private static HttpListener httpListener = null;
public FrmResponse()
{
InitializeComponent();
}
/// <summary>
/// HttpListener 监听http的回调函数
/// </summary>
/// <param name="ar"></param>
private void GetHttpListenerCallBack(IAsyncResult ar)
{
try
{
httpListener = ar.AsyncState as HttpListener;
HttpListenerContext context = httpListener.EndGetContext(ar);
httpListener.BeginGetContext(new AsyncCallback(GetHttpListenerCallBack), httpListener);
var request = context.Request;
var response = context.Response;
response.ContentType = "text/plain;charset=UTF-8"; // 告诉客户端返回的ContentType类型为纯文本格式,编码为UTF-8
response.AddHeader("Content-type", "text/plain"); // 添加响应头信息
response.ContentEncoding = Encoding.UTF8;
string returnObj = null; // 定义返回客户端的信息
if (request.HttpMethod == "POST" && request.ContentLength64 > 0)
{
// 处理客户端发送的请求并返回处理信息
returnObj = HandleRequest(request, response);
}
else
{
returnObj = $"必须是POST请求,并且必须传参";
response.StatusDescription = "400";
response.StatusCode = 400;
}
var returnByteArr = Encoding.UTF8.GetBytes(returnObj); // 设置客户端返回信息的编码
try
{
using (var stream = response.OutputStream)
{
// 把处理信息返回到客户端
stream.Write(returnByteArr, 0, returnByteArr.Length);
ShowMsg($"HTTP请求响应内容:{returnObj}");
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"发生异常:{ex.ToString()}");
ShowMsg($"发生异常:{ ex.ToString()}");
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"发生异常:{ex.ToString()}");
ShowMsg($"发生异常:{ ex.ToString()}");
}
}
private string HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
// 获取请求参数
NameValueCollection myCol = request.QueryString;
var contentType = request.ContentType;
try
{
// 获取Post请求中的参数和值帮助类
HttpListenerPostParaHelper httppost = new HttpListenerPostParaHelper(request);
// 获取Post过来的参数和数据
List<HttpListenerPostValue> lst = httppost.GetHttpListenerPostValue();
var data = "";
// 获取body中的参数
foreach (var key in lst)
{
if (key.type == 0)
{
string value = Encoding.UTF8.GetString(key.datas).Replace("\r\n", "");
if (key.name == "QUERY_STRING") // 参数名
{
data = value;
Console.WriteLine(value);
}
}
}
response.StatusDescription = "200"; // 获取或设置返回给客户端的 HTTP 状态代码的文本说明。
response.StatusCode = 200; // 获取或设置返回给客户端的 HTTP 状态代码。
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"接收数据完成:{data.Trim()},时间:{DateTime.Now.ToString()}");
ShowMsg($"接收数据完成:{data.Trim()}");
// 获取得到数据data可以进行其他操作
return $"接收数据完成";
}
catch (Exception ex)
{
response.StatusDescription = "404";
response.StatusCode = 404;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"在接收数据时发生错误:{ex.ToString()}");
return $"在接收数据时发生错误:{ex.ToString()}"; // 把服务端错误信息直接返回可能会导致信息不安全,此处仅供参考
}
}
private void btn_开启HTTP监听_Click(object sender, EventArgs e)
{
if (httpListener == null)
{
httpListener = new HttpListener();
httpListener.Prefixes.Add($"{txt_接收端地址.Text.Trim()}"); // http://xxx.xxx.xxx.xxx:8082/base/saveData/
httpListener.Start();
httpListener.BeginGetContext(new AsyncCallback(GetHttpListenerCallBack), httpListener);
ShowMsg($"{txt_接收端地址.Text.Trim()} HTTP监听已开启");
}
}
private delegate void dlgShowMsg(string msg);
private void ShowMsg(string msg)
{
if (richTextBox1.InvokeRequired)
{
dlgShowMsg dlg = new dlgShowMsg(ShowMsg);
richTextBox1.Invoke(dlg, msg);
}
else
{
if (richTextBox1.Lines.Length >= 1000)
richTextBox1.Clear();
richTextBox1.AppendText($"{Environment.NewLine}{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {msg}");
}
}
private void btn_清空HTTP日志_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
}
}
}
解析POST请求中的参数,写了一个 HttpListenerPostParaHelper 帮助类,代码如下:
#region << 版 本 注 释 >>
/*----------------------------------------------------------------
* 版权所有 (c) 2021 保留所有权利。
* CLR版本:4.0.30319.42000
* 机器名称:DESKTOP-T90SFH8
* 公司名称:
* 命名空间:HttpListenerDemo
* 唯一标识:a871efaa-2645-4503-bc99-8e05369ba4c9
* 文件名:HttpListenerPostParaHelper
* 当前用户域:DESKTOP-T90SFH8
*
* 创建者:Cgs
* 电子邮箱:1003590782@qq.com
* 创建时间:10/14/2021 9:40:45 AM
* 版本:V1.0.0
* 描述:
*
* ----------------------------------------------------------------
* 修改人:
* 时间:
* 修改说明:
*
* 版本:V1.0.1
*----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace HttpListenerDemo
{
/// <summary>
/// HttpListenerPostParaHelper 的摘要说明
/// </summary>
internal class HttpListenerPostParaHelper
{
#region <变量>
private HttpListenerRequest request;
#endregion <变量>
#region <方法>
public HttpListenerPostParaHelper(HttpListenerRequest request)
{
this.request = request;
}
private bool CompareBytes(byte[] source, byte[] comparison)
{
try
{
int count = source.Length;
if (source.Length != comparison.Length)
return false;
for (int i = 0; i < count; i++)
if (source[i] != comparison[i])
return false;
return true;
}
catch
{
return false;
}
}
private byte[] ReadLineAsBytes(Stream SourceStream)
{
var resultStream = new MemoryStream();
while (true)
{
int data = SourceStream.ReadByte();
resultStream.WriteByte((byte)data);
if (data == 10)
break;
}
resultStream.Position = 0;
byte[] dataBytes = new byte[resultStream.Length];
resultStream.Read(dataBytes, 0, dataBytes.Length);
return dataBytes;
}
/// <summary>
/// 获取Post过来的参数和数据
/// </summary>
/// <returns></returns>
public List<HttpListenerPostValue> GetHttpListenerPostValue()
{
try
{
List<HttpListenerPostValue> HttpListenerPostValueList = new List<HttpListenerPostValue>();
if (request.ContentType.Length > 20 && string.Compare(request.ContentType.Substring(0, 20), "multipart/form-data;", true) == 0)
{
string[] HttpListenerPostValue = request.ContentType.Split(';').Skip(1).ToArray();
string boundary = string.Join(";", HttpListenerPostValue).Replace("boundary=", "").Trim();
byte[] ChunkBoundary = Encoding.UTF8.GetBytes("--" + boundary + "\r\n");
byte[] EndBoundary = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");
Stream SourceStream = request.InputStream;
var resultStream = new MemoryStream();
bool CanMoveNext = true;
HttpListenerPostValue data = null;
while (CanMoveNext)
{
byte[] currentChunk = ReadLineAsBytes(SourceStream);
if (!Encoding.UTF8.GetString(currentChunk).Equals("\r\n"))
resultStream.Write(currentChunk, 0, currentChunk.Length);
if (CompareBytes(ChunkBoundary, currentChunk))
{
byte[] result = new byte[resultStream.Length - ChunkBoundary.Length];
resultStream.Position = 0;
resultStream.Read(result, 0, result.Length);
CanMoveNext = true;
if (result.Length > 0)
data.datas = result;
data = new HttpListenerPostValue();
HttpListenerPostValueList.Add(data);
resultStream.Dispose();
resultStream = new MemoryStream();
}
else if (Encoding.UTF8.GetString(currentChunk).Contains("Content-Disposition"))
{
byte[] result = new byte[resultStream.Length - 2];
resultStream.Position = 0;
resultStream.Read(result, 0, result.Length);
CanMoveNext = true;
data.name = Encoding.UTF8.GetString(result).Replace("Content-Disposition: form-data; name=\"", "").Replace("\"", "").Split(';')[0];
resultStream.Dispose();
resultStream = new MemoryStream();
}
else if (Encoding.UTF8.GetString(currentChunk).Contains("Content-Type"))
{
CanMoveNext = true;
data.type = 1;
resultStream.Dispose();
resultStream = new MemoryStream();
}
else if (CompareBytes(EndBoundary, currentChunk))
{
byte[] result = new byte[resultStream.Length - EndBoundary.Length - 2];
resultStream.Position = 0;
resultStream.Read(result, 0, result.Length);
data.datas = result;
resultStream.Dispose();
CanMoveNext = false;
}
}
}
return HttpListenerPostValueList;
}
catch (Exception ex)
{
return null;
}
}
#endregion <方法>
}
/// <summary>
/// HttpListenner监听Post请求参数值实体
/// </summary>
public class HttpListenerPostValue
{
/// <summary>
/// 0:参数
/// 1:文件
/// </summary>
public int type = 0;
public string name;
public byte[] datas;
}
}
3.下载例程
该例程演示了HttpListener、HttpWebRequest的应用,包括如何解析POST请求中Body的数据(multipart/form-data)、如何构造multipart/form-data; boundary=的ContentType等进行文件、参数上传等技巧:https://download.csdn.net/download/CGS_______/33070416