WinForms嵌入浏览器中拦截WebSocket数据的实现方法
拦截内嵌浏览器WebSocket推送数据的方案
一、用WebView2控件(推荐,基于Edge Chromium)
WebView2的扩展能力更强,拦截WebSocket数据主要有两种实用方法:
1. 注入JavaScript拦截消息
核心思路是在页面加载时注入脚本,重写WebSocket的onmessage方法,把收到的数据转发给WinForms程序,同时不影响页面原有逻辑。
代码示例:
private async void WebView21_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) { if (e.IsSuccess) { // 注入拦截WebSocket的JS脚本 string injectScript = @" const OriginalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { const ws = new OriginalWebSocket(url, protocols); // 保存原始的onmessage方法 const originalOnMessage = ws.onmessage; ws.onmessage = function(event) { // 把数据发回WinForms window.chrome.webview.postMessage({ type: 'ws-data', content: event.data, wsUrl: ws.url }); // 调用原始方法,保证页面正常运行 if (originalOnMessage) originalOnMessage.call(this, event); }; return ws; }; "; await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(injectScript); // 监听WebView2发来的消息 webView21.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived; } } private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e) { // 解析JSON格式的消息 var message = System.Text.Json.JsonSerializer.Deserialize<WsMessage>(e.WebMessageAsJson); if (message.Type == "ws-data") { // 在这里处理数据 HandleWsData(message.Content); } } // 辅助类用于解析消息 public class WsMessage { public string Type { get; set; } public string Content { get; set; } public string WsUrl { get; set; } } private void HandleWsData(string data) { // 你的计算处理逻辑,比如解析、运算 Console.WriteLine($"拿到WebSocket数据:{data}"); }
2. 网络请求拦截(进阶)
如果需要更底层的控制,可以通过CoreWebView2.AddWebResourceRequestedFilter拦截WebSocket的握手请求,再监听后续的传输帧,但这种方法复杂度高,一般场景用第一种就够了。
二、用WebBrowser控件(IE内核)
WebBrowser基于旧版IE,拦截方式类似但需要借助COM互操作:
- 给Form类加上
[ComVisible(true)]特性,让JS能调用WinForms方法 - 在页面加载完成后注入JS,重写WebSocket的
onmessage,把数据传给WinForms
代码示例:
[ComVisible(true)] public partial class MainForm : Form { public MainForm() { InitializeComponent(); // 允许JS调用WinForms对象 webBrowser1.ObjectForScripting = this; } private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) { if (webBrowser1.Document != null) { string injectScript = @" var OriginalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { var ws = new OriginalWebSocket(url, protocols); var originalOnMessage = ws.onmessage; ws.onmessage = function(event) { // 调用WinForms的方法传数据 window.external.GetWsData(event.data, ws.url); if (originalOnMessage) originalOnMessage.call(this, event); }; return ws; }; "; webBrowser1.Document.InvokeScript("eval", new object[] { injectScript }); } } // 供JS调用的公开方法 public void GetWsData(string data, string url) { HandleWsData(data); } private void HandleWsData(string data) { // 数据处理逻辑 Console.WriteLine($"收到WebSocket数据:{data}"); } }
关于自行创建WebSocket连接的问题
如果服务器支持多连接,用C#的ClientWebSocket直接连是可行的,但要注意:
- 服务器可能限制单IP连接数,或者需要页面WebSocket的会话信息(比如Cookie、Token),自行连接可能会失败
- 优先用上面的拦截方法,因为它复用了浏览器已建立的连接,不需要额外处理验证、会话等问题,更稳定
如果要测试自行连接,可以用这段代码:
private async Task TestWsConnection(string targetIp) { using (var ws = new ClientWebSocket()) { try { // 替换成实际的WebSocket路径,比如ws://xxx/ws Uri wsUri = new Uri($"ws://{targetIp}/your-ws-path"); await ws.ConnectAsync(wsUri, CancellationToken.None); Console.WriteLine("连接成功"); // 循环接收数据 byte[] buffer = new byte[4096]; while (ws.State == WebSocketState.Open) { var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Text) { string data = Encoding.UTF8.GetString(buffer, 0, result.Count); HandleWsData(data); } } } catch (Exception ex) { Console.WriteLine($"连接失败:{ex.Message}"); // 大概率是服务器不支持多连接或者需要验证 } } }
内容的提问来源于stack exchange,提问作者Leon
相关产品推荐
相关产品推荐

