万金流
以码会友。 吾Q:578751655。 水平有限,轻喷,谢!

写在前面:

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

毕竟主推SignalR。

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

…………

写完这篇文章之后,我挺诧异的:就没人在底层基础上进行封装吗?

翻了一会儿nuget官网,终于找到一个用着比较舒服的包,“jchristn/WatsonWebsocket”。还比较新,网站显示,2023年6月更新过。

至此,释怀了。耐心不好的读者看后面三分之一吧。

 …………

在MDN(Mozilla Developer Network)官网上,有一篇文档,讲了使用“TcpListener”接受websocket请求的写法。感觉这个包,是沿用了这个思路。有兴趣的读者也可以参考尝试。


步骤描述:

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行的代码,基本来自官方例程。我不求甚解,大家随意。

至此,服务端完成。


使用Nuget上的WatsonWebsocket包,构建服务器。

注意:

1、VS需要用管理员权限启动,不然无法启动服务。

2、这个包,是用guid标识用户的。发送消息的时候请注意。

3、构造函数中的false,表示不适用SSL协议。

以下代码,基本上涵盖了它的常用功能。

 1 using System.Text;
 2 using WatsonWebsocket;
 3 
 4 namespace ConsoleApp1
 5 {
 6     internal class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             WatsonWsServer server = new WatsonWsServer("192.168.1.104", 8080, false);
11             server.ClientConnected += ClientConnected;
12             server.ClientDisconnected += ClientDisconnected;
13             server.MessageReceived += MessageReceived;
14             server.Start();
15             Console.WriteLine("server is running...");
16             Console .ReadKey();
17             server.Stop();
18 
19             void ClientConnected(object? sender, ConnectionEventArgs args)
20             {
21                 Console.WriteLine("Client connected: " + args.Client.ToString());
22             }
23 
24             void ClientDisconnected(object? sender, DisconnectionEventArgs args)
25             {
26                 Console.WriteLine("Client disconnected: " + args.Client.ToString());
27             }
28 
29             async void MessageReceived(object? sender, MessageReceivedEventArgs args)
30             {
31                 string s = Encoding.UTF8.GetString(args.Data);
32                 Console.WriteLine("server's clients:" + server.ListClients().Count()+ ": " + s);
33                 await server.SendAsync(args.Client.Guid,"from server:"+s);
34             }
35         }
36     }
37 }

 

 


 

两个服务端的客户端,都可以完全照搬 使用js和nodejs完成websocket双向通讯 里的web客户端。注意下服务器地址即可。

微软官网服务器运行结果:

 

 含WatsonWebsocket包的服务器运行结果:

  成功,调试通过。

posted on 2023-12-17 18:23  万金流  阅读(459)  评论(0编辑  收藏  举报