使用JS和C#完成websocket双向通讯

发布时间 2023-12-17 18:24:04作者: 万金流

写在前面:

微软官方对websocket的直接支持很差,教程也写得不用心。还要用户自己去转字节数组和字符串,太过分了!

毕竟主推SignalR。

本文是在官方教程的基础上,对其进行了一些简单的讲解,和方法提取、封装,以期降低学习难度。


步骤描述:

1、随便建了个普通的mvc项目(任意带控制器的.net项目道理相同,都应该可以)。在Program.cs中,添加websocket支持。见以下代码第24行。

 

 1 namespace WebApplication2
 2 {
 3     public class Program
 4     {
 5         public static void Main(string[] args)
 6         {
 7             var builder = WebApplication.CreateBuilder(args);
 8 
 9             // Add services to the container.
10             builder.Services.AddControllersWithViews();
11 
12             var app = builder.Build();
13 
14             // Configure the HTTP request pipeline.
15             if (!app.Environment.IsDevelopment())
16             {
17                 app.UseExceptionHandler("/Home/Error");
18             }
19             app.UseStaticFiles();
20 
21             app.UseRouting();
22 
23             //启用websocket
24             app.UseWebSockets();
25             //app.UseAuthorization();
26 
27             app.MapControllerRoute(
28                 name: "default",
29                 pattern: "{controller=Home}/{action=Index}/{id?}");
30 
31             app.Run();
32         }
33     }
34 }

只支持ws或wss请求。

2、控制器代码如下。14行注册路由(即要以类似 ws://xxxx/ws 的方式访问)

 1 using Microsoft.AspNetCore.Http;
 2 using Microsoft.AspNetCore.Mvc;
 3 using System.Net.WebSockets;
 4 using System.Text;
 5 using WebApplication2.Function;
 6 
 7 namespace WebApplication2.Controllers
 8 {
 9     //[Route("api/[controller]")]
10     //[ApiController]
11     public class WebSocketController : ControllerBase
12     {
13         //注册路由
14         [Route("/ws")]
15         public async Task Get()
16         {
17             //如果是websocket请求
18             if (HttpContext.WebSockets.IsWebSocketRequest)
19             {
20                 //获得连接
21                 using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
22                 //处理程序
23                 await 处理程序(webSocket);
24             }
25             else
26             {
27                 HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
28             }
29         }
30 
31         async Task 处理程序(WebSocket w)
32         {
33             string s;
34             bool f;
35             //简单的处理,收消息,修改,发送。
36             (f,s)=await WebSocket操作.收(w);
37             while(f)
38             {
39                 s = "server:" + s;
40                 await WebSocket操作.发(w, s);
41                 (f,s)= await WebSocket操作.收(w);
42             }
43             await WebSocket操作.关(w);
44         }
45     }
46 }

注释都比较清楚,不赘述。

31行开始的处理程序,从设计的角度,不应该出现在控制器里。介意的读者,请自行处理。

WebSocket操作类,见后面。

3、WebSocket操作类:

 1 using System;
 2 using System.Net.WebSockets;
 3 using System.Text;
 4 
 5 namespace WebApplication2.Function
 6 {
 7     //对微软官方示例的简单封装,
 8     public class WebSocket操作
 9     {
10         //接收消息,bool指示收到的是不是关闭消息,string是收到的内容。
11         public static async Task<(bool,string)> 收(WebSocket ws)
12         {
13             //接收大小最多4k。
14             var buffer = new byte[1024 * 4];
15             string s;
16             //把消息收到字节数组中
17             var receiveResult = await ws.ReceiveAsync(
18                 new ArraySegment<byte>(buffer), CancellationToken.None);
19             //如果不是关闭消息
20             if (!receiveResult.CloseStatus.HasValue)
21             {
22                 //转换为字符串返回
23                 s = Encoding.UTF8.GetString(buffer, 0, receiveResult.Count);
24                 return (true,s);
25             }
26             else
27             {
28                 s = "";
29                 return (false,s);
30             }
31         }
32 
33         //发送消息
34         public static async Task 发(WebSocket ws, string s)
35         {
36             //消息转换成字节数组
37             var buffer = Encoding.UTF8.GetBytes(s);
38             //发送
39             await ws.SendAsync(
40                 new ArraySegment<byte>(buffer, 0, buffer.Length),
41                 WebSocketMessageType.Text,
42                 true,
43                 CancellationToken.None);
44         }
45         public static async Task 关(WebSocket ws)
46         {
47             //官方例子里,status是接收到的关闭类型。这里偷懒,应该也不影响用。
48             var status = ws.CloseStatus == null ? WebSocketCloseStatus.Empty :ws.CloseStatus.Value;
49             //关闭
50             await ws.CloseAsync(
51                 status,
52                 ws.CloseStatusDescription,
53                 CancellationToken.None);
54         }
55     }
56 }

 其中17-20,39-44,50-53行的代码,基本来自官方例程。我不求甚解,大家随意。

至此,服务端完成。


客户端,可以完全照搬 使用js和nodejs完成websocket双向通讯 里的web客户端。

运行结果:

 成功,调试通过。