.NET 基于Socket中转WebSocket

前言

针对IOS App Proxy Server无法直连WebSocket,建立 Socket中转端。

WebSocket

WebSocket 端用于实现实时通信功能。

WebSocket 端通过 WebSocket 协议与中转端通信,中转端可以通过 WebSocket 或其他传输协议与 WebSocket 端建立连接,收发消息。

定义 SendMessageToChatRoomIP 和 JoinChatRoom,用于处理来自客户端的消息和连接事件。

中转端 C1 (Socket Server)

中转端 C1 是一个独立的应用程序,用于连接WebSocket端,建立Socket服务。

中转端 C1 的主要功能是接收来自客户端 C2 的消息,并将其转发给 WebSocket 端,或者接收来自 WebSocket 端的消息,并转发给客户端 C2。

客户端 C2 x  N

客户端 C2 ,主要与中转端连接 发送数据,接收数据。

在客户端 C2 中,连接到中转端 C1、发送消息给中转端 C1、接收来自中转端 C1 的消息。

调用关系为:

客户端 C2 通过与中转端 C1 的通信,向 WebSocket 端发送消息或接收来自 WebSocket 端的消息。

中转端 C1 接收来自客户端 C2 的消息,并将其转发给 WebSocket 端,或者接收来自 WebSocket 端的消息,并转发给客户端 C2。

中转端实现

1.连接WebSocket

      public async Task ConnectToServer()
      {
          try
          {
              connection = new HubConnectionBuilder()
                  .WithUrl("xxx/chathub")//
                  .Build();

              connection.On<string, string, string, string>("ReceiveMessage", async (user, message, clientIP, chatRoomId) =>
              {
                  Console.WriteLine($"Received message from wsserver_sr : {user}: {message} |socketserver {clientIP}|{chatRoomId}");

                  // 收到来自服务器的消息时,向客户端 C2 发送消息
                  await SendMessageToClientC2(user, message, clientIP, chatRoomId);
              });

              await connection.StartAsync();
          }
          catch (Exception ex)
          {
              Console.WriteLine($"Error connecting to SignalR server: {ex.Message}" + Environment.NewLine);
          }
      }

 收到消息时发送给对应客户端

       public async Task SendMessageToClientC2(string user, string message, string clientIP, string chatRoomId)
       {
           // 根据 chatRoomId 找到对应的客户端 C2,并发送消息
           if (chatRooms.ContainsKey(chatRoomId))
           {
               byte[] data = Encoding.ASCII.GetBytes($"{user}  {message}  |{clientIP}|{chatRoomId} ");
               foreach (var client in chatRooms[chatRoomId])
               {
                   await client.GetStream().WriteAsync(data, 0, data.Length);
               }
           }
           else
           {
               Console.WriteLine($"Chat room {chatRoomId} does not exist" + Environment.NewLine);
           }
       }

2.建立中转端Socket端口

   public void StartListening()
   {
       if (!listening)
       {
           try
           {
               int port = 10086; // 监听端口
               server = new TcpListener(IPAddress.Any, port);
               server.Start();
               listening = true;
               Console.WriteLine($"Server listening on port {port}" + Environment.NewLine);

               Task.Run(() => AcceptClients());
           }
           catch (Exception ex)
           {
               Console.WriteLine($"Error starting server: {ex.Message}" + Environment.NewLine);
           }
       }
   }

3.向客户端发消息


        public async Task AcceptClients()
        {
            while (listening)
            {
                try
                {
                    TcpClient client = await server.AcceptTcpClientAsync();
                    Task.Run(() => HandleClient(client));
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error accepting client: {ex.Message}" + Environment.NewLine);
                }
            }
        }

4.监听客户端连接

   public async Task AcceptClients()
   {
       while (listening)
       {
           try
           {
               TcpClient client = await server.AcceptTcpClientAsync();
               Task.Run(() => HandleClient(client));
           }
           catch (Exception ex)
           {
               Console.WriteLine($"Error accepting client: {ex.Message}" + Environment.NewLine);
           }
       }
   }

5.处理客户端连接

    public async Task HandleClient(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        while (client.Connected)
        {
            try
            {
                bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                ProcessMessage(message, client);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error receiving message: {ex.Message}" + Environment.NewLine);
                break;
            }
        }

        // 客户端断开连接时,从聊天室中移除
        foreach (var chatRoomId in chatRooms.Keys)
        {
            if (chatRooms[chatRoomId].Contains(client))
            {
                chatRooms[chatRoomId].Remove(client);
                Console.WriteLine($"Client disconnected from chat room {chatRoomId}" + Environment.NewLine);
                break;
            }
        }
    }

6.处理连接

        public async void ProcessMessage(string message, TcpClient client)
        {
            string[] parts = message.Split('|');
            if (parts.Length >= 2)
            {
                string command = parts[0];
                string chatRoomId = parts[1];

                switch (command)
                {
                    case "JoinChatRoom":
                        await JoinChatRoom(chatRoomId, client);
                        break;
                    case "SendMessageToChatRoomIP2":
                        if (parts.Length >= 4)
                        {
                            string user = parts[2];
                            string msg = parts[3];
                            //  await SendMessageToChatRoom(chatRoomId, user, msg);
                            await SendMessageToChatRoomAddress(chatRoomId, user, msg, client);
                        }
                        break;
                    default:
                        Console.WriteLine($"Invalid command: {command}" + Environment.NewLine);
                        break;
                }
            }
        }

JoinChatRoom 异步调用服务端的JoinChatRoom 

SendMessageToChatRoomIP2 异步调用SendMessage  

7.运行

本地运行

发布到linux

run  Server listening on port 10860

netstat -tln查看端口 (tuln) 

也可以优化封装成WinForm程序,收发消息可视化。

点击启动socket端口调用 1.连接websocket 2.监听socket   

也可以封装成系统服务,与进程一样藏在后台运行。

客户端实现

1.连接Socket端

   private async void MainForm_Load(object sender, EventArgs e)
   {
       try
       {
           client = new TcpClient();
           await client.ConnectAsync("192.168.80.123", 10086); 
           stream = client.GetStream();
           connected = true;

           // 连接成功后,调用 JoinChatRoom 方法加入聊天室
           await JoinChatRoom("room001");

           Task.Run(ReceiveMessages);
       }
       catch (Exception ex)
       {
           textBoxReceivedMessages.AppendText($"Error connecting to server: {ex.Message}" + Environment.NewLine);
       }
   }

 2.加入ChatRoom

JoinChatRoom中转到Server端再加入聊天室

   private async Task JoinChatRoom(string chatRoomId)
        {
            // 发送消息给中转端 C1,请求加入聊天室
            byte[] data = Encoding.ASCII.GetBytes($"JoinChatRoom|{chatRoomId}|J");
            await stream.WriteAsync(data, 0, data.Length);
        }

3.发送消息

发送到Socket中转端

     
        private async Task SendMessageToChatRoom(string chatRoomId, string message)
        {
            // 发送消息给中转端 C1,请求向指定聊天室发送消息
            byte[] data = Encoding.ASCII.GetBytes($"SendMessageToChatRoomIP2|{chatRoomId}|client2m|{message}");
            await stream.WriteAsync(data, 0, data.Length);
        }

        

4.接收消息

private async Task ReceiveMessages()
        {
            byte[] buffer = new byte[1024];
            int bytesRead;

            while (connected)
            {
                try
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                    DisplayMessage(message);
                }
                catch (Exception ex)
                {
                    textBoxReceivedMessages.AppendText($"Error receiving message: {ex.Message}" + Environment.NewLine);
                }
            }
        }

        private void DisplayMessage(string message)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string>(DisplayMessage), message);
                return;
            }

            textBoxReceivedMessages.AppendText(message + Environment.NewLine);
        }

     

5.点击事件

Send

   private async void buttonSend_Click(object sender, EventArgs e)
        {
            if (!connected)
            {
                textBoxReceivedMessages.AppendText("Not connected to server." + Environment.NewLine);
                return;
            }

            string message = textBoxSendMessage.Text;

            if (!string.IsNullOrEmpty(message))
            {
                // 发送消息给中转端 C1,请求向聊天室发送消息
                await SendMessageToChatRoom("room001", message);
                textBoxSendMessage.Clear();
                textBoxReceivedMessages.AppendText("Sent message to chat room: room001"  + Environment.NewLine);
            }
            else
            {
                textBoxReceivedMessages.AppendText("Please enter a message." + Environment.NewLine);
            }
        }

6.运行

服务端启动

Server listening on port 10086

服务端发收发消息到中转端

Sent message to chat room: room001
client2m  hihihi|socketclient 192.168.80.123  |::ffff:183.7.113.105|room001 
 

中转端拿到客户端信息->WebSocket端->中转端接收->客户端接收

Client joined chat room room001
Received message from WSSserver_sr : client2m: hihihi|socketclient 192.168.80.123 |socketserver:::ffff:183.7.113.105|room001

客户端也可以用IOS SOCKET5发送消息

//...         
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    NSData * sendData = [@"JoinChatRoom|room001" dataUsingEncoding:NSUTF8StringEncoding];
                    [self.socket writeData:sendData withTimeout:-1 tag:1];
                    
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        NSData * sendData = [@"SendMessageToChatRoomIP2|room001|userid001|hello" dataUsingEncoding:NSUTF8StringEncoding];
                        [self.socket writeData:sendData withTimeout:-1 tag:2];
                        [self.socket readDataWithTimeout:-1 tag:1];
                    });
                });

//...

Run

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/571783.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【工具】录屏软件Captura安装使用及ffmpeg下载配置

开启技术视频创作&#xff0c;录屏软件林林总总&#xff0c;适合的、习惯的最好。 录屏软件Captura的使用及ffmpeg下载配置 1.Captura下载、安装2.FFmpeg下载、配置3.Captura屏幕录制试用、录制视频效果 1.Captura下载、安装 Captura主要是一个免费开源的录屏软件&#xff0c…

2024年新算法-鹦鹉优化器(PO)优化BP神经网络回归预测

2024年新算法-鹦鹉优化器(PO)优化BP神经网络回归预测 亮点&#xff1a; 输出多个评价指标&#xff1a;R2&#xff0c;RMSE&#xff0c;MSE&#xff0c;MAPE和MAE 满足需求&#xff0c;分开运行和对比的都有对应的主函数&#xff1a;main_BP, main_PO, main_BPvsBP_PO&#x…

洛谷 P1021 邮票面值设计

原题链接&#xff1a;[NOIP1999 提高组] 邮票面值设计 - 洛谷 目录 题目描述 解题思路&#xff1a; 代码实现&#xff1a; 题后总结&#xff1a; 题目描述 给定一个信封&#xff0c;最多只允许粘贴 N 张邮票&#xff0c;计算在给定 K&#xff08;NK≤15&#xff09;种邮票…

javaWeb项目-邮票鉴赏系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Java技术 Java 程…

spring boot3单模块项目工程搭建-下(个人开发模板)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 上文衔接 常用依赖介绍以及整合 web组件 测试组件 样板代码生成 数据库连接器 常用工具包 面向切面编…

浅涉ROS世界中的坐标系及其他

声明&#xff1a;文中图片素材均采用了其他博主文章&#xff08;文末参考来源&#xff09;&#xff0c;如有侵权或不妥&#xff08;确有不妥和不安&#xff0c;奈何苦于佳图难觅&#xff09;&#xff0c;还望告知&#xff0c;立即删除&#xff01; 坐标系统 ROS中的…

【Stable Diffusion系列】(一):AI绘画本地部署教程

目录 一、总览 二、本地部署 1、安装cuda 2、安装python 3、安装git 4、方法一 1&#xff09;获取安装包 2&#xff09;update 3&#xff09;run 5、方法二 1&#xff09;git clone 2&#xff09;双击webui-user.bat 3&#xff09;更新 6、设置启动参数 7、…

【linux】进程地址被占用

在强制关闭一个udp程序后&#xff0c;重启该程序报错&#xff1a; bind error: Address already in use 查找并关闭占用端口的进程&#xff1a; 首先&#xff0c;确定哪个进程占用了目标端口。在Linux系统中&#xff0c;可以使用以下命令&#xff1a; netstat -tulnp | grep …

ArcGIS无法开始编辑TIN!开始编辑TIN显示灰色

ArcGIS无法开始编辑TIN&#xff01;开始编辑TIN显示灰色&#xff1f; 解决方案&#xff01; 1、确认自定义——扩展模块中空间分析、3D分析模块勾选。 2、确认以上后&#xff0c;还是不能编辑的话&#xff0c;我们可以调出 3D分析分析工具条&#xff0c;你就会发现。TIN编辑工…

Paddle 1.8 与 Paddle 2.0 API 映射表

安装2.6的paddlepaddle之后总是报fluid的错误&#xff0c;查询得知这个接口已经弃用了&#xff0c;但是一直找不到替换接口&#xff0c;偶然查询报错信息的时候找到了映射表&#xff0c;转存一下。 Paddle 1.8 与 Paddle 2.0 API 映射表

在React函数组件中使用错误边界和errorElement进行错误处理

在React 18中,函数组件可以使用两种方式来处理错误: 使用 ErrorBoundary ErrorBoundary 是一种基于类的组件,可以捕获其子组件树中的任何 JavaScript 错误,并记录这些错误、渲染备用 UI 而不是冻结的组件树。 在函数组件中使用 ErrorBoundary,需要先创建一个基于类的 ErrorB…

SAM在低阶自适应航空土地覆盖分类中的应用2024.01

GEOSCIENCE AND REMOTE SENSING LETTERS 2024.01 提出了一种新的语义分割模型&#xff0c;该模型结合了SAM的图像编码器和低秩自适应方法(LoRA)&#xff0c;用于航空图像的特征提取和微调。我们还使用了一个辅助CNN编码器来促进下游适应&#xff0c;并补充ViT编码器在密集视觉…

探索visionOS基础知识:创建应用程序图标

每当您使用不同的 Apple 平台时,您都会注意到必须学习如何为其设计本机应用程序图标。无论是 iOS、macOS 还是 tvOS,每个平台都有适合该特定平台的独特规范。 VisionOS 要求创建美观、三维、独特的应用程序图标,使主视图上感觉熟悉且逼真。 对于与 VisionOS 兼容的现有 …

js 连接快手打印组件并实现打印

快手打印组件文档&#xff1a; https://docs.qingque.cn/d/home/eZQA41D2h9LGUFaD26bC07e–?identityIdEmukFTnlEF#sectionh.kgnfm4rjc89m 快手打印组件下载&#xff1a; https://docs.qingque.cn/d/home/eZQBMOMSj4mJ5D7Xplofq-p4Y?identityIdEmukFTnlEF 快手打印数据格式&…

在ubuntu上搭建nexus私有仓库(指定版本以及jdk!)

前言 本来以为搭建一个nexus随随便便就好了&#xff0c;但是遇到了最新版本根本没办法在jdk17下面正常运行—起码我调了一下不知道怎么运行&#xff0c;我才知道。。。不升级版本其实是很有道理的。 这一篇是最新版本的尝试&#xff1a; 在ubuntu上搭建nexus私有仓库[失败草稿…

图片hover放大效果

实现效果&#xff1a;一张图片&#xff0c;鼠标放上去时&#xff0c;出现放大效果 非常简单&#xff0c;两个关键词&#xff1a;hover和transform 对应的代码结构如下图 框架背景&#xff1a; Tips: transform结合不同的参数可以实现元素的位移、旋转、缩放 如果有任何疑问或…

针对icon报错

针对上篇文章生成图标链接中图标报错 C# winfrom应用程序添加图标-CSDN博客 问题&#xff1a;参数“picture”必须是可用作Icon的参数 原因&#xff1a;生成的ico图标类型不匹配 解决方法&#xff1a; 更改导出的ico类型

国产3D自研技术如何突围?眸瑞科技给3D建设、管理带来全新模式

眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&#xff0c;它专注于让一切3D模型在全网多端轻量化处理与展示&#xff0c;为行业数字化转型升级与数字孪生应用提供成套的国产自研3D可视化技术、产品与服务。 引言 眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&…

绿色便携方式安装apache+mysql+tomcat+php集成环境并提供控制面板

绿色便携方式安装带控制面板的ApacheMariaDBTomcatPHP集成环境 目录 绿色便携方式安装带控制面板的ApacheMariaDBTomcatPHP集成环境[TOC](目录) 前言一、XAMPP二、安装和使用1.安装2.使用 三、可能的错误1、检查端口占用2、修改端口 前言 安装集成环境往往配置复杂&#xff0c…

DHCP原理和配置

1、DHCP原理 &#xff08;1&#xff09;什么是DHCP DHCP(Dynamic HostConfiguration Protocol,动态主机配置协议)&#xff1a;给网络内的客户机自动分配IP地址由internet工作任务小组设计开发口专门用于为TCP/IP网络中的计算机自动分配TCP/IP参数的协议DHCP采用的是UDP作为传输…
最新文章