TCP客户端模拟链接websocket服务端

因一些特殊原因研究了下TCP模拟链接websocket。原理上可以连接但具体怎么连接怎么操作就不知道了,需要研究下,以下是个人研究的方案。

用线上和本地地址来做例子:

线上wss地址:wss://server.cs.com/cs/vido/1

本地地址ws://127.0.0.1/cs/vido/1

如果用本地地址来模拟websocket,需要遵从websocket协议。WebSocket协议需要通过已建立的TCP连接来传输数据。具体实现上是通过http协议建立通道,然后在此基础上用真正的WebSocket协议进行通信,所以WebSocket协议和http协议是有一定的交叉关系的。

在tcp与websocket消息交互之前需要先和websocket建立握手关系:

cs 复制代码
GET /cs/vido HTTP/1.1
Host: server.cs.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

Get输出的是ws后面的子路径地址

Host输出的是域名地址

upgrade、Connection输出的是要升级到websocket通讯

Sec-WebSocket-Key输出的是Base64 encode 的值,这个是随机生成的。

Sec-WebSocket-Version输出的是websocket的版本,默认必须是13

websocket服务器收到客户端消息后会返回验证消息;

cs 复制代码
HTTP/1.1 101 
Connection: upgrade
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Upgrade: websocket
Sec-WebSocket-Accept: sT7MD7zMs7k3yPTFV6JvttwPwoE=

客户端接收到服务端发送的新的Sec-WebSocket-Accept参数后,使用原来的随机密钥和新的Sec-WebSocket-Accept参数共同生成一个新的Sec-WebSocket-Key参数,用于加密数据传输。

客户端将新的Sec-WebSocket-Key参数发送给服务端,服务端接收到后,使用该参数加密数据传输,收到此消息后是代表已经和websocket建立了联系。

这块要注意下,如果是用本地地址去测试没问题,但用正式地址去测试就会被提示需要ssl加密。

使用ssl加密代码如下:

cs 复制代码
 _tcpClient = new TcpClient(host, port);
            _networkStream = _tcpClient.GetStream();


            // 创建SslStream
            SslStream sslStream = new SslStream(_networkStream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            // 验证服务器证书
            sslStream.AuthenticateAsClient(host);


  // 验证服务器证书的回调函数
        public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
            return false;
        }

用了ssl加密和没用ssl加密的获取的消息也是不一样的,如果用了ssl加密后就要用sslStream来接收消息或者发送消息否则会乱码。没有用ssl加密那就用_networkStream来接收或者发送消息。

整体代码如下:

cs 复制代码
public class WebSocketClient2
    {
        private TcpClient _tcpClient;
        private NetworkStream _networkStream;

        public void ConnectToWebSocket(string host, int port, string resource)
        {
            _tcpClient = new TcpClient(host, port);
            _networkStream = _tcpClient.GetStream();


            // 创建SslStream
            SslStream sslStream = new SslStream(_networkStream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            // 验证服务器证书
            sslStream.AuthenticateAsClient(host);

            // 构建WebSocket握手消息
            string key = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));
         
         

            string handshakeMessage = $"GET {resource} HTTP/1.1\r\n" +
                                    $"Host:{host}\r\n" +
                                    "Upgrade: websocket\r\n" +
                                    "Connection: Upgrade\r\n" +
                                    $"Sec-WebSocket-Key: {key}\r\n" +
                                    "Sec-WebSocket-Version: 13\r\n" +
                                    "\r\n";
            // 发送握手消息
            byte[] handshakeBuffer = Encoding.UTF8.GetBytes(handshakeMessage);
            //_networkStream.Write(handshakeBuffer, 0, handshakeBuffer.Length);
            sslStream.Write(handshakeBuffer, 0, handshakeBuffer.Length);
            // 接收服务器响应并验证
            byte[] buffer = new byte[1024];
            //int bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
            int bytesRead = sslStream.Read(buffer, 0, buffer.Length);
            string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
            Console.WriteLine(response);


            // 验证服务器响应的Sec-WebSocket-Accept
            string expectedResponse = Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
            if (response.Contains($"Sec-WebSocket-Accept: {expectedResponse}"))
            {
                Console.WriteLine("WebSocket handshake successful.");
            }
            else
            {
                Console.WriteLine("WebSocket handshake failed.");
            }

            byte[] buffer2 = new byte[1024];
            int bytesRead2;
            while ((bytesRead2 = sslStream.Read(buffer2, 0, buffer2.Length)) > 0)
            {
                string message = Encoding.UTF8.GetString(buffer2, 0, bytesRead2);
                // 解析WebSocket消息
                // ...
                Console.WriteLine($"Received message: {message}");
            }
        }

        
        // 验证服务器证书的回调函数
        public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
            return false;
        }
    }




//代码实现调用
  WebSocketClient2 client = new WebSocketClient2();
            client.ConnectToWebSocket("server.cs.com.cn", 443, "/cs/vido/1");//测试环境ws端口号默认是80,wss默认端口号是443
相关推荐
“抚琴”的人6 小时前
【机械视觉】C#+VisionPro联合编程———【六、visionPro连接工业相机设备】
c#·工业相机·visionpro·机械视觉
FAREWELL000757 小时前
C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法
学习·c#·面向对象·运算符重载·oop·拓展方法
CodeCraft Studio8 小时前
Excel处理控件Spire.XLS系列教程:C# 合并、或取消合并 Excel 单元格
前端·c#·excel
忆源9 小时前
SOME/IP-SD -- 协议英文原文讲解9(ERROR处理)
网络·网络协议·tcp/ip
勘察加熊人9 小时前
forms实现连连看
c#
hvinsion9 小时前
PPT助手:一款集计时、远程控制与多屏切换于一身的PPT辅助工具
c#·powerpoint·ppt·ppt助手·ppt翻页
weixin_3077791311 小时前
使用C#实现从Hive的CREATE TABLE语句中提取分区字段名和数据类型
开发语言·数据仓库·hive·c#
时光追逐者12 小时前
在 Blazor 中使用 Chart.js 快速创建数据可视化图表
开发语言·javascript·信息可视化·c#·.net·blazor
与火星的孩子对话13 小时前
Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发
人工智能·unity·c#·游戏引擎·语音识别·宠物