layspain21 2025-11-20 13:29 采纳率: 50%
浏览 8
已结题

关于#c与JS通信的问题,请各位专家解答!

C#用动态库 Xilium.CefGlue,与JS通信,本地得index.html 网页能进行监听,服务端网址 http://192.168.11.67:11002/examAdvice 无法监听,到底那里得问题

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace CWebSocket
{
    public class WebSocket
    {
        private Dictionary<string, Session> SessionPool = new Dictionary<string, Session>();
        private Dictionary<string, string> MsgPool = new Dictionary<string, string>();
        private static WebSocket webSocket = new WebSocket();

        public static WebSocket Instance
        {
            get { return webSocket; }
        }

        #region 启动WebSocket服务
        /// <summary>
        /// 启动WebSocket服务
        /// </summary>
        public void start(int port)
        {
            //Socket SockeServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unknown);
            Socket SockeServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unspecified);
            SockeServer.Bind(new IPEndPoint(IPAddress.Any, port));
            SockeServer.Listen(20);
            SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);            
        }
        #endregion

        #region 处理客户端连接请求
        /// <summary>
        /// 处理客户端连接请求
        /// </summary>
        /// <param name="result"></param>
        private void Accept(IAsyncResult socket)
        {
            // 还原传入的原始套接字
            Socket SockeServer = (Socket)socket.AsyncState;
            // 在原始套接字上调用EndAccept方法,返回新的套接字
            Socket SockeClient = SockeServer.EndAccept(socket);
            byte[] buffer = new byte[4096];
            try
            {
                //接收客户端的数据
                SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
                //保存登录的客户端
                Session session = new Session();
                session.SockeClient = SockeClient;
                session.IP = SockeClient.RemoteEndPoint.ToString();
                session.buffer = buffer;
                lock (SessionPool)
                {
                    if (SessionPool.ContainsKey(session.IP))
                    {
                        this.SessionPool.Remove(session.IP);
                    }
                    this.SessionPool.Add(session.IP, session);
                }
                //准备接受下一个客户端
                SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
                Console.WriteLine(string.Format("Client {0} connected", SockeClient.RemoteEndPoint));
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error : " + ex.ToString());
            }
        }
        #endregion

        #region 处理接收的数据
        /// <summary>
        /// 处理接受的数据
        /// </summary>
        /// <param name="socket"></param>
        private void Recieve(IAsyncResult socket)
        {
            Socket SockeClient = (Socket)socket.AsyncState;
            string IP = SockeClient.RemoteEndPoint.ToString();
            if (SockeClient == null || !SessionPool.ContainsKey(IP))
            {
                return;
            }
            try
            {
                int length = SockeClient.EndReceive(socket);
                byte[] buffer = SessionPool[IP].buffer;
                SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
                string msg = Encoding.UTF8.GetString(buffer, 0, length);
                //  websocket建立连接的时候,除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作
                if (msg.Contains("Sec-WebSocket-Key"))
                {
                    SockeClient.Send(PackageHandShakeData(buffer, length));
                    SessionPool[IP].isWeb = true;
                    return;
                }
                if (SessionPool[IP].isWeb)
                {
                    msg = AnalyzeClientData(buffer, length);
                }
                
                if (!string.IsNullOrEmpty(msg))
                {
                    Console.WriteLine("=======>" + msg);
                    Message message = Newtonsoft.Json.JsonConvert.DeserializeObject<Message>(msg);
                    HandlerHelper.call(SockeClient, message);
                }                
            }
            catch
            {
                try
                {
                    SockeClient.Disconnect(true);
                    Console.WriteLine("客户端 {0} 断开连接", IP);
                    SessionPool.Remove(IP);
                }
                catch { }                
            }
        }
        #endregion

        #region 发送消息到客户端
        public void CallJsMethod(string method, string data, JsCallResultDelegate callResult = null)
        {
            try
            {
                List<Socket> clients = new List<Socket>();
                foreach (Session se in SessionPool.Values)
                {
                    clients.Add(se.SockeClient);
                }

                CallJsMethod(clients, method, data, callResult);
            }
            catch(Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            }            
        }

        public void CallJsMethod(Session sess, string method, string data, JsCallResultDelegate callResult = null)
        {
            try
            {
                List<Socket> clients = new List<Socket>();
                clients.Add(sess.SockeClient);
                CallJsMethod(clients, method, data, callResult);
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            } 
        }

        public void CallJsMethod(List<Socket> clients, string method, string data, JsCallResultDelegate callResult = null)
        {
            foreach (Socket client in clients)
            {
                CallJsMethod(client, method, data, callResult);
            }
        }

        public void CallJsMethod(Socket sockeClient, string method, string data, JsCallResultDelegate callResult = null)
        {
            try
            {
                Message message = new Message();
                message.Url = "__call__." + method;
                message.Data = data;
                string msg = Newtonsoft.Json.JsonConvert.SerializeObject(message);      

                Send(sockeClient, msg);

                if (null != callResult)
                {
                    HandlerHelper.RegisterJsReturn(method, callResult);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            } 
        }

        public void Send(string url, string data)
        {
            try
            {
                List<Socket> clients = new List<Socket>();
                foreach (Session se in SessionPool.Values)
                {
                    clients.Add(se.SockeClient);
                }

                Send(clients, url, data);
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            }
        }

        public void Send(Session sess, string url, string data)
        {
            try
            {
                List<Socket> clients = new List<Socket>();
                clients.Add(sess.SockeClient);
                Send(clients, url, data);
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            }
        }

        public void Send(List<Socket> clients, string url, string data)
        {
            foreach (Socket client in clients)
            {
                Send(client, url, data);
            }
        }

        public void Send(Socket sockeClient, string url, string data)
        {
            try
            {
                Message message = new Message();
                message.Url = url;
                message.Data = data;
                string msg = Newtonsoft.Json.JsonConvert.SerializeObject(message);

                Send(sockeClient, msg);
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            }
        }

        private void Send(Socket sockeClient, string msg)
        {
            try
            {
                byte[] msgBuffer = PackageServerData(msg);
                sockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送数据到页面端异常:" + ex.Message);
            }
        }
        #endregion

        #region 客户端和服务端的响应
        /*
         * 客户端向服务器发送请求
         * 
         * GET / HTTP/1.1
         * Origin: http://localhost:1416
         * Sec-WebSocket-Key: vDyPp55hT1PphRU5OAe2Wg==
         * Connection: Upgrade
         * Upgrade: Websocket
         *Sec-WebSocket-Version: 13
         * User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
         * Host: localhost:8064
         * DNT: 1
         * Cache-Control: no-cache
         * Cookie: DTRememberName=admin
         * 
         * 服务器给出响应
         * 
         * HTTP/1.1 101 Switching Protocols
         * Upgrade: websocket
         * Connection: Upgrade
         * Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=
         * 
         * 在请求中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个魔幻字符串
         * “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端
         */
        #endregion

        #region 打包请求连接数据
        /// <summary>
        /// 打包请求连接数据
        /// </summary>
        /// <param name="handShakeBytes"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] PackageHandShakeData(byte[] handShakeBytes, int length)
        {
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length);
            string key = string.Empty;
            Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = reg.Match(handShakeText);
            if (m.Value != "")
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            string secKey = Convert.ToBase64String(secKeyBytes);
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");
            responseBuilder.Append("Upgrade: websocket" + "\r\n");
            responseBuilder.Append("Connection: Upgrade" + "\r\n");
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }
        #endregion

        #region 处理接收的数据
        /// <summary>
        /// 处理接收的数据
        /// 参考 http://www.cnblogs.com/smark/archive/2012/11/26/2789812.html
        /// </summary>
        /// <param name="recBytes"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private string AnalyzeClientData(byte[] recBytes, int length)
        {
            int start = 0;
            // 如果有数据则至少包括3位
            if (length < 2) return "";
            // 判断是否为结束针
            bool IsEof = (recBytes[start] >> 7) > 0;
            // 暂不处理超过一帧的数据
            if (!IsEof) return "";
            start++;
            // 是否包含掩码
            bool hasMask = (recBytes[start] >> 7) > 0;
            // 不包含掩码的暂不处理
            if (!hasMask) return "";
            // 获取数据长度
            UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F;
            start++;
            // 存储4位掩码值
            byte[] Masking_key = new byte[4];
            // 存储数据
            byte[] mDataPackage;
            if (mPackageLength == 126)
            {
                // 等于126 随后的两个字节16位表示数据长度
                mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]);
                start += 2;
            }
            if (mPackageLength == 127)
            {
                // 等于127 随后的八个字节64位表示数据长度
                mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]);
                start += 8;
            }
            mDataPackage = new byte[mPackageLength];
            for (UInt64 i = 0; i < mPackageLength; i++)
            {
                mDataPackage[i] = recBytes[i + (UInt64)start + 4];
            }
            Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4);
            for (UInt64 i = 0; i < mPackageLength; i++)
            {
                mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]);
            }
            return Encoding.UTF8.GetString(mDataPackage);
        }
        #endregion

        #region 发送数据
        /// <summary>
        /// 把发送给客户端消息打包处理(拼接上谁什么时候发的什么消息)
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="message">Message.</param>
        private byte[] PackageServerData(string msg)
        {
            byte[] content = null;
            byte[] temp = Encoding.UTF8.GetBytes(msg);
            if (temp.Length < 126)
            {
                content = new byte[temp.Length + 2];
                content[0] = 0x81;
                content[1] = (byte)temp.Length;
                Buffer.BlockCopy(temp, 0, content, 2, temp.Length);
            }
            else if (temp.Length < 0xFFFF)
            {
                content = new byte[temp.Length + 4];
                content[0] = 0x81;
                content[1] = 126;
                content[2] = (byte)(temp.Length & 0xFF);
                content[3] = (byte)(temp.Length >> 8 & 0xFF);
                Buffer.BlockCopy(temp, 0, content, 4, temp.Length);
            }
            return content;
        }
        #endregion
    }
}


using CWebSocket;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Xilium.CefGlue;
using Xilium.CefGlue.Form;

namespace TestJsCall
{
    public partial class Form1 : Form
    {

        Xilium.CefGlue.Client.CefWebBrowser browser = new Xilium.CefGlue.Client.CefWebBrowser();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            WebSocket.Instance.start( 7766);
            WebSocket.Instance.start(11002);

            // 注册处理类
            // param1 命名空间名称
            // param2 处理类
            // test  这里注册了两个测试处理类
            HandlerHelper.RegisterHandler("pc", new PcHandler());
            HandlerHelper.RegisterHandler("form", new FormOperaHandler());

            browser.Dock = DockStyle.Fill;
            String path = "file:///" + Application.StartupPath + "/index.html";
            path = Regex.Replace(path, @"\\", "/");
            path = Regex.Replace(path, @"#", "%23");

            //path = @"http://192.168.11.67:11002/examAdvice?docCode=000443&deptCode=2002&branchCode=001&clinicRegNo=ZY010000713082&tenantCode=42017976-4-0002";
            //path = "www.baidu.com";
            browser.StartUrl = path;
            panel1.Controls.Add(browser);

        }
}
}


  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-20 13:43
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    关于你提到的 C# 与 JavaScript(JS)通信问题,特别是使用 Xilium.CefGlue 进行通信时,本地网页(index.html)可以监听,但服务端网址 http://192.168.11.67:11002/examAdvice 无法监听,这是一个典型的网络通信或 WebSocket 配置问题。


    🚨 问题分析

    1. C# WebSocket 服务配置问题

    你目前使用的是一个自定义的 WebSocket 服务器实现,可能是基于 Socket 类手动处理 WebSocket 协议。这种做法虽然可行,但容易出错,尤其是在协议握手、数据格式解析等方面。

    注意: 使用 Xilium.CefGlue 通常用于嵌入 Chromium 浏览器,其与 JS 的通信通常是通过 CefSharp.JSObjectCefSharp.IWebBrowserExecuteJavaScriptAsync 方法实现的,而不是直接通过 WebSocket。


    ✅ 解决方案

    一、确认你的目标

    你是否想让 C# 程序前端页面(JS) 通信?如果是这样:

    • 如果是 本地网页(index.html),可以通过 CefSharpRegisterJsObject 注册 C# 对象;
    • 如果是 远程网页(如 http://192.168.11.67:11002/examAdvice),需要确保该网页在同一个网络中,并且能访问到 C# 服务。

    二、解决思路(重点)

    1. 使用 CefSharp 实现 C# 与 JS 通信

    如果你使用的是 CefSharp(基于 Xilium.CefGlue),推荐使用以下方式实现通信:

    重点: 通过 RegisterJsObject 注册 C# 对象,供 JS 调用。

    public class MyCSharpClass
    {
        public void ShowMessage(string message)
        {
            Console.WriteLine("Received from JS: " + message);
        }
    }
    
    // 在浏览器初始化后注册对象
    browser.RegisterJsObject("myCSharp", new MyCSharpClass());
    

    在 HTML 中调用:

    window.myCSharp.ShowMessage("Hello from JS");
    

    2. 如果确实要使用 WebSocket

    如果你仍然希望使用 WebSocket 来进行通信,那么你需要:

    ✅ 检查以下几点:
    1. 防火墙设置

      • 确保 192.168.11.67:11002 端口在防火墙中开放;
      • 检查路由器是否允许外部访问该 IP 和端口(如果是内网)。
    2. WebSocket 服务是否正确启动

      • 查看控制台日志,确认服务已成功绑定并监听;
      • 使用 telnet 192.168.11.67 11002 测试端口是否可达。
    3. 客户端是否正确连接

      • 确保 JS 使用了正确的 WebSocket URL,例如:
        const ws = new WebSocket("ws://192.168.11.67:11002/");
        
    4. WebSocket 握手是否成功

      • 检查是否完成了 WebSocket 握手流程(包括 Sec-WebSocket-Key 的处理);
      • 你当前的代码中 PackageHandShakeData 可能没有正确生成响应头。

    三、修复建议(修改后的 WebSocket 代码)

    以下是修正后的 WebSocket 服务部分代码(仅展示关键部分):

    private byte[] PackageHandShakeData(byte[] buffer, int length)
    {
        string key = Encoding.UTF8.GetString(buffer, 0, length);
        Regex regex = new Regex("Sec-WebSocket-Key: (.*)");
        Match match = regex.Match(key);
        if (match.Success)
        {
            string secKey = match.Groups[1].Value.Trim();
            string acceptKey = secKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            using (SHA1 sha1 = SHA1.Create())
            {
                byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(acceptKey));
                string response = Convert.ToBase64String(hash);
                return Encoding.UTF8.GetBytes(
                    "HTTP/1.1 101 Switching Protocols\r\n" +
                    "Upgrade: websocket\r\n" +
                    "Connection: Upgrade\r\n" +
                    "Sec-WebSocket-Accept: " + response + "\r\n\r\n"
                );
            }
        }
        return null;
    }
    

    注意: 上述代码应放在握手逻辑中,确保只对第一次请求进行处理。


    四、检查 C# WebSocket 服务是否运行正常

    你可以添加以下日志:

    Console.WriteLine($"Server started on port {port}");
    

    并在浏览器中测试访问:

    curl http://192.168.11.67:11002/
    

    如果返回空,说明服务未正确响应;如果返回 HTML 内容,则可能不是 WebSocket 服务。


    🔍 最终建议

    | 问题 | 解决方案 | |------|----------| | 本地网页可以监听,但服务端不能 | 确认是否为 WebSocket 服务未正确启动,或者防火墙阻止了访问 | | C# 与 JS 通信失败 | 推荐使用 CefSharpRegisterJsObject 注册对象,而非手动实现 WebSocket | | WebSocket 握手失败 | 确保 Sec-WebSocket-Key 正确解析并生成响应头 |


    ✅ 总结

    • 推荐使用 CefSharp 的内置 JS 通信机制,避免手动实现 WebSocket;
    • 检查防火墙和端口开放情况,确保远程网页能访问 C# 服务;
    • 调试时打印日志,确认 WebSocket 是否成功握手。

    如果你愿意提供完整的 C# WebSocket 代码和 HTML 页面代码,我可以进一步帮你定位具体错误。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 11月28日
  • 已采纳回答 11月20日
  • 修改了问题 11月20日
  • 创建了问题 11月20日