在实际项目中通讯的交互的过程中,遇见数据传输时同事和我说用WebApi,Get,Post等等,以前没用搞过这个块的我傻傻分不清。查询资料了解过后发现只是个简单的Http交互通讯,在此记录下Winform中的简单服务器和客户端的编写,并简要描述webapi与http通讯的差异。
HTTP(Hypertext Transfer Protocol)是一种用于在网络上传输超文本的协议。它是一个用于传输和交换超文本、图像、视频、音频以及其他多媒体资源的应用层协议。
HTTP是Web的基础,它定义了客户端和服务器之间的通信规则。通常,当你在浏览器中输入网址、点击链接或者提交表单时,浏览器会发送HTTP请求到服务器,服务器收到请求后进行处理,然后返回HTTP响应给客户端(浏览器),最终在你的浏览器上显示网页内容。
HTTP协议的主要特点包括:
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace httpServer
{
class HttpServer_DP
{
private readonly string mUrl; // 服务器监听的URL
private readonly HttpListener mListener; // HttpListener实例
private readonly Dictionary<string, Func<HttpListenerContext, Task<string>>> mRoutes; // 路由映射
// 构造函数,接收服务器监听地址和端口的数组
public HttpServer_DP(string[] prefixes)
{
if (!HttpListener.IsSupported)
{
throw new NotSupportedException("HttpListener is not supported on this platform.");
}
mListener = new HttpListener(); // 初始化HttpListener实例
mRoutes = new Dictionary<string, Func<HttpListenerContext, Task<string>>>(); // 初始化路由映射字典
// 为服务器添加监听地址和端口
foreach (string prefix in prefixes)
{
mListener.Prefixes.Add(prefix);
}
mUrl = prefixes[0]; // 记录第一个监听地址
}
public bool IsOpen { get { return mListener.IsListening; } }
// 启动服务器
public void Start()
{
mListener.Start();
Console.WriteLine($"Server started and listening on {mUrl}");
mListener.BeginGetContext(ProcessRequestCallback, mListener); // 处理客户端请求
}
// 停止服务器
public void Stop()
{
mListener.Stop();
mListener.Close();
Console.WriteLine("Server stopped.");
}
// 添加路由和处理程序的映射关系
public void AddRoute(string route, Func<HttpListenerContext, Task<string>> handler)
{
mRoutes.Add(route, handler);
}
// 处理客户端请求的回调函数
private async void ProcessRequestCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
// 开始下一个请求的监听
listener.BeginGetContext(ProcessRequestCallback, listener);
try
{
// 获取传入的请求
HttpListenerContext context = listener.EndGetContext(result);
// 获取请求方法和URL路径
string httpMethod = context.Request.HttpMethod;
string responseString = "No Data!"; // 默认响应字符串
string url = context.Request.Url.AbsolutePath;
Func<HttpListenerContext, Task<string>> handler;
// 如果请求路径存在于路由映射中,执行相应的处理程序
if (mRoutes.TryGetValue(url, out handler))
{
responseString = await handler(context); // 获取处理程序返回的响应数据
// 将响应数据编码成字节数组
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// 设置响应的内容长度和状态码
context.Response.ContentLength64 = buffer.Length;
context.Response.StatusCode = (int)HttpStatusCode.OK;
// 将响应写入输出流并关闭输出流
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
}
else
{
// 如果请求路径不存在于路由映射中,返回404 Not Found
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing request: {ex.Message}");
}
}
}
}
需要注意的是:Dictionary<string, Func<HttpListenerContext, Task>> mRoutes; // 路由映射
为了方便根据客户端传入的URL的不同路径来选择进行不同的处理流程
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace Http
{
class HttpClient_DP
{
private readonly HttpClient _client; // HttpClient 实例
// 构造函数,初始化 HttpClient 实例
public HttpClient_DP()
{
_client = new HttpClient();
}
// 设置超时时间
public TimeSpan TimeOut { set { _client.Timeout = value; } }
// 发送 GET 请求并返回响应内容
public async Task<string> Get(string url)
{
HttpResponseMessage response = await _client.GetAsync(url); // 发送 GET 请求
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync(); // 读取响应内容
return content;
}
else
{
return $"Error: {response.StatusCode}"; // 返回错误信息
}
}
// 发送 POST 请求并返回响应内容
public async Task<string> Post(string url, string data)
{
HttpContent content = new StringContent(data); // 创建包含请求数据的 HttpContent 实例
HttpResponseMessage response = await _client.PostAsync(url, content); // 发送 POST 请求
if (response.IsSuccessStatusCode)
{
string responseData = await response.Content.ReadAsStringAsync(); // 读取响应内容
return responseData;
}
else
{
return $"Error: {response.StatusCode}"; // 返回错误信息
}
}
}
}
其中只实现的Get与Post的相关代码,这也是当前我最常用的两个方法。
其中 public TimeSpan TimeOut为超时时间,不设置时将永久等待服务器响应。当响应超时时,外部可以通过(try-catch)捕获TaskCanceledException异常。
初始化服务器:
httpServer.HttpServer_DP HttpServer;
public static void IniHttpServer()
{
HttpServer = new httpServer.HttpServer_DP(new string[]
{
"http://127.0.0.1:8080/"
});
//绑定映射,处理函数
//当传入URL为"http://127.0.0.1:8080/PostAGVC_Data/"时将调用PostAGVC_Data函数接收和解析
HttpServer.AddRoute("/PostAGVC_Data/", PostAGVC_Data);
//当传入URL为"http://127.0.0.1:8080/PostAGV_Data/"时将调用PostAGV_Data函数接收和解析
HttpServer.AddRoute("/PostAGV_Data/", PostAGV_Data);
HttpServer.Start();
}
public static async Task<string> PostAGVC_Data(HttpListenerContext context)
{
string httpMethod = context.Request.HttpMethod;
string responseString = "";
// 处理 POST 请求
if (context.Request.HasEntityBody)
{
// 从请求主体中获取数据
using (System.IO.Stream body = context.Request.InputStream)
{
using (System.IO.StreamReader reader = new System.IO.StreamReader(body, context.Request.ContentEncoding))
{
string postData = reader.ReadToEnd(); // 读取 POST 数据
//处理数据
//。。。。。。。。。。。。
//。。。。。。。。。。。。
//返回字符串
responseString = "OK";
}
}
}
return responseString;
}
//与PostAGVC_Data内部类似,处理数据的代码不一样
public static async Task<string> PostAGV_Data(HttpListenerContext context)
{
//省略
}
由于Http的无连接性,客户端只需要实例化后,需要时进行连接就行,简单的初始化(如果需要的话)如下:
Http.HttpClient_DP HttpClient ;
public static void IniHttpClient()
{
if (HttpClient == null)
{
HttpClient = new Http.HttpClient_DP();
HttpClient.TimeOut = TimeSpan.FromSeconds(10);
}
}
调用:
其中JsonConvert只是将我的类实例序列化成Json字符串,在此可以仅当字符串来看。
string ret = await Global_Value.HttpClient.Post(url, JsonConvert.SerializeObject(new PLC_WorkState()
{
name = agvName,
workState = workState
}));
在WinForms中编写的HTTP服务器类与WebAPI之间有几个主要区别:
1.目的和设计用途:
2.功能和特性:
3.集成和扩展性:
4.适用场景:
WinForms HTTP服务器类: 适用于需要在WinForms应用程序中实现简单的HTTP通信或提供基本服务的情况。
WebAPI: 更适合构建大型的Web应用程序或服务,提供多种HTTP服务,满足各种复杂的需求,例如构建RESTfulAPI供移动应用或Web前端使用。
总体来说,WinForms中的简单HTTP服务器类适用于简单的、局部的HTTP通信需求,而WebAPI更适用于构建和管理完整的Web服务或RESTful API,并提供了更多现成的工具和功能来简化开发流程。