介绍
我喜欢.NET CORE 这个东西,其实不仅仅源于它性能很高,可以跨平台,还因为它的设计模式确实令人着迷。以前没.NET CORE 的时候,.NET用websocket必须跑在windows server 2012上,但我一般不会这么干,都把websocket架在nodejs的服务器上。这么分出来,麻烦肯定是麻烦的,而且js这东西,写复杂和几年后再看都是头疼的问题。那么,如果.NET CORE是以kestrel运行的,那么就不再需要考虑服务器的版本运行,任何一个地方都可以用websocket
ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理。但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容。
在Microsoft的文档中,我找到了一个很好的WebSockets工作示例。它仍然是管理连接,以便能够从一个连接向其他连接广播消息,这是SignalR开箱即用的功能。期望这个逻辑非常复杂,我想从Startup类中删除它。
背景
要阅读ASP.NET Core中的WebSockets支持,可以在此处查看。如果您想了解中间件以及如何在ASP.NET Core中编写它,请阅读此链接。
代码使用
首先,你必须添加 Microsoft.AspNetCore.WebSockets
包到你的项目。
现在,您可以创建一个扩展方法和类来管理WebSockets:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
public static class WebSocketExtensions
{
public static IApplicationBuilder UseCustomWebSocketManager( this IApplicationBuilder app)
{
return app.UseMiddleware<CustomWebSocketManager>();
}
}
public class CustomWebSocketManager
{
private readonly RequestDelegate _next;
public CustomWebSocketManager(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)
{
if (context.Request.Path == "/ws" )
{
if (context.WebSockets.IsWebSocketRequest)
{
string username = context.Request.Query[ "u" ];
if (! string .IsNullOrEmpty(username))
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
CustomWebSocket userWebSocket = new CustomWebSocket()
{
WebSocket = webSocket,
Username = username
};
wsFactory.Add(userWebSocket);
await wsmHandler.SendInitialMessages(userWebSocket);
await Listen(context, userWebSocket, wsFactory, wsmHandler);
}
}
else
{
context.Response.StatusCode = 400;
}
}
await _next(context);
}
private async Task Listen(HttpContext context, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)
{
WebSocket webSocket = userWebSocket.WebSocket;
var buffer = new byte [1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync( new ArraySegment< byte >(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await wsmHandler.HandleMessage(result, buffer, userWebSocket, wsFactory);
buffer = new byte [1024 * 4];
result = await webSocket.ReceiveAsync( new ArraySegment< byte >(buffer), CancellationToken.None);
}
wsFactory.Remove(userWebSocket.Username);
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
}
|
在这种情况下,WebSockets请求在URL中始终包含“/ ws”。查询字符串包含用于将WebSocket与登录用户相关联的用户名的参数u。
CustomWebSocket是一个包含WebSocket和用户名的类:
1
2
3
4
5
|
public class CustomWebSocket
{
public WebSocket WebSocket { get ; set ; }
public string Username { get ; set ; }
}
|
我也创建了自定义WebSocket消息:
1
2
3
4
5
6
7
|
class CustomWebSocketMessage
{
public string Text { get ; set ; }
public DateTime MessagDateTime { get ; set ; }
public string Username { get ; set ; }
public WSMessageType Type { get ; set ; }
}
|
其中Type是您可能拥有的不同类型消息的枚举。
在Startup类中,您必须注册以下服务:
1
2
|
services.AddSingleton<ICustomWebSocketFactory, CustomWebSocketFactory>();
services.AddSingleton<ICustomWebSocketMessageHandler, CustomWebSocketMessageHandler>();
|
CustomWebSocketFactory负责收集连接的WebSockets列表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
public interface ICustomWebSocketFactory
{
void Add(CustomWebSocket uws);
void Remove( string username);
List<CustomWebSocket> All();
List<CustomWebSocket> Others(CustomWebSocket client);
CustomWebSocket Client( string username);
}
public class CustomWebSocketFactory : ICustomWebSocketFactory
{
List<CustomWebSocket> List;
public CustomWebSocketFactory()
{
List = new List<CustomWebSocket>();
}
public void Add(CustomWebSocket uws)
{
List.Add(uws);
}
//when disconnect
public void Remove( string username)
{
List.Remove(Client(username));
}
public List<CustomWebSocket> All()
{
return List;
}
public List<CustomWebSocket> Others(CustomWebSocket client)
{
return List.Where(c => c.Username != client.Username).ToList();
}
public CustomWebSocket Client( string username)
{
return List.First(c=>c.Username == username);
}
}
|
CustomWebSocketMessageHandler包含有关消息的逻辑(即在连接时需要发送任何消息以及如何对传入消息作出反应)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
public interface ICustomWebSocketMessageHandler
{
Task SendInitialMessages(CustomWebSocket userWebSocket);
Task HandleMessage(WebSocketReceiveResult result, byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
Task BroadcastOthers( byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
Task BroadcastAll( byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
}
public class CustomWebSocketMessageHandler : ICustomWebSocketMessageHandler
{
public async Task SendInitialMessages(CustomWebSocket userWebSocket)
{
WebSocket webSocket = userWebSocket.WebSocket;
var msg = new CustomWebSocketMessage
{
MessagDateTime = DateTime.Now,
Type = WSMessageType.anyType,
Text = anyText,
Username = "system"
};
string serialisedMessage = JsonConvert.SerializeObject(msg);
byte [] bytes = Encoding.ASCII.GetBytes(serialisedMessage);
await webSocket.SendAsync( new ArraySegment< byte >(bytes, 0, bytes.Length), WebSocketMessageType.Text, true , CancellationToken.None);
}
public async Task HandleMessage(WebSocketReceiveResult result, byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
{
string msg = Encoding.ASCII.GetString(buffer);
try
{
var message = JsonConvert.DeserializeObject<CustomWebSocketMessage>(msg);
if (message.Type == WSMessageType.anyType)
{
await BroadcastOthers(buffer, userWebSocket, wsFactory);
}
}
catch (Exception e)
{
await userWebSocket.WebSocket.SendAsync( new ArraySegment< byte >(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}
}
public async Task BroadcastOthers( byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
{
var others = wsFactory.Others(userWebSocket);
foreach (var uws in others)
{
await uws.WebSocket.SendAsync( new ArraySegment< byte >(buffer, 0, buffer.Length), WebSocketMessageType.Text, true , CancellationToken.None);
}
}
public async Task BroadcastAll( byte [] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
{
var all = wsFactory.All();
foreach (var uws in all)
{
await uws.WebSocket.SendAsync( new ArraySegment< byte >(buffer, 0, buffer.Length), WebSocketMessageType.Text, true , CancellationToken.None);
}
}
}
|
最后,在Configure方法的Startup类中添加以下内容:
1
2
3
4
5
6
7
8
|
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 4 * 1024
};
app.UseWebSockets(webSocketOptions);
app.UseCustomWebSocketManager();
|
通过这种方式,Starup类保持干净,管理WebSockets的逻辑可以扩展,使您可以根据自己的喜好灵活地组织它。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:http://www.nopapp.com/Blog/Article/middleware-websocket-aspnetcore