在WinForms应用中集成HTTP与WebSocket服务
创建支持HTTP和WebSocket通信的桌面应用程序
本文介绍如何在 .NET WinForms 项目中同时实现 HTTP 接口和 WebSocket 服务,使传统桌面程序具备网络通信能力,可被外部系统通过标准协议调用或实时交互。
1. 创建 WinForms 项目
使用 Visual Studio 新建一个 Windows Forms 应用(.NET Framework 或 .NET)。项目创建完成后,即可开始添加网络功能模块。
2. 实现轻量级HTTP服务器
利用 HttpListener 类构建内嵌 HTTP 服务,无需依赖 IIS。以下为封装后的服务类:
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
public class EmbeddedHttpServer
{
private readonly HttpListener _listener = new HttpListener();
private readonly Dictionary<string, Func<HttpListenerContext, Task<string>>> _routes;
public EmbeddedHttpServer(string[] prefixes)
{
if (!HttpListener.IsSupported)
throw new PlatformNotSupportedException("当前操作系统不支持内建HTTP服务");
foreach (var prefix in prefixes)
_listener.Prefixes.Add(prefix);
_routes = new Dictionary<string, Func<HttpListenerContext, Task<string>>>();
}
public void Start()
{
_listener.Start();
ListenForRequests();
}
public void Stop()
{
_listener.Stop();
_listener.Close();
}
public void MapEndpoint(string path, Func<HttpListenerContext, Task<string>> handler)
{
_routes[path] = handler;
}
private async void ListenForRequests()
{
while (_listener.IsListening)
{
try
{
var context = await _listener.GetContextAsync();
_ = HandleRequestAsync(context);
}
catch (ObjectDisposedException)
{
break;
}
catch (Exception ex)
{
// 可选:记录异常日志
}
}
}
private async Task HandleRequestAsync(HttpListenerContext ctx)
{
var path = ctx.Request.Url.AbsolutePath;
var method = ctx.Request.HttpMethod;
string responseText = "{\"error\":\"未找到匹配接口\"}";
int statusCode = 404;
if (_routes.TryGetValue(path, out var handler) && method == "POST")
{
responseText = await handler(ctx);
statusCode = 200;
}
var buffer = Encoding.UTF8.GetBytes(responseText);
ctx.Response.StatusCode = statusCode;
ctx.Response.ContentType = "application/json; charset=utf-8";
ctx.Response.ContentLength64 = buffer.Length;
await ctx.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
ctx.Response.Close();
}
}
3. 集成WebSocket服务
通过 NuGet 安装 WebSocketSharp-NetStandard 包(版本 1.0.1),用于提供双向通信能力。
4. 构建WebSocket服务管理器
定义统一入口来控制 WebSocket 服务生命周期:
using WebSocketSharp.Server;
public class WsServiceManager
{
private static WebSocketServer _server;
static WsServiceManager()
{
_server = new WebSocketServer(8888);
_server.AddWebSocketService<MessageHandler>("/echo");
}
public static bool IsRunning => _server?.IsListening == true;
public static void Launch()
{
_server.Start();
}
public static void Shutdown()
{
_server.Stop();
}
}
5. 定义消息处理行为
继承 WebSocketBehavior 实现具体业务逻辑:
using WebSocketSharp;
using WebSocketSharp.Server;
public class MessageHandler : WebSocketBehavior
{
protected override void OnOpen()
{
Send("连接已建立");
}
protected override void OnMessage(MessageEventArgs e)
{
var reply = e.Data switch
{
"ping" => "pong",
_ => $"收到: {e.Data}"
};
Send(reply);
}
protected override void OnClose(CloseEventArgs e)
{
// 连接关闭时清理资源
}
}
6. 在主窗体中初始化服务
在 Form1 的构造函数中启动各项服务:
public partial class Form1 : Form
{
private EmbeddedHttpServer _httpServer;
public Form1()
{
InitializeComponent();
InitializeServices();
}
private void InitializeServices()
{
// 启动HTTP服务
_httpServer = new EmbeddedHttpServer(new[] { "http://127.0.0.1:1314/" });
_httpServer.MapEndpoint("/api/data", ProcessDataRequest);
_httpServer.Start();
// 启动WebSocket服务
WsServiceManager.Launch();
}
private async Task<string> ProcessDataRequest(HttpListenerContext ctx)
{
using var reader = new StreamReader(ctx.Request.InputStream, ctx.Request.ContentEncoding);
var body = await reader.ReadToEndAsync();
return "{\"status\":\"success\",\"data\":\"操作完成\"}";
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_httpServer?.Stop();
WsServiceManager.Shutdown();
}
base.Dispose(disposing);
}
}
7. 测试WebSocket客户端
使用 HTML 页面测试连接:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket 测试</title>
</head>
<body>
<button onclick="connect()">连接服务器</button>
<script>
function connect() {
const ws = new WebSocket("ws://localhost:8888/echo");
ws.onopen = () => {
console.log("连接成功");
ws.send("Hello Server!");
};
ws.onmessage = (event) => {
console.log("返回:", event.data);
};
ws.onclose = () => {
alert("连接断开");
};
}
</script>
</body>
</html>