[英]How to share a websocket between services in asp net core
我正在使用中间件在.net核心上设置websocket(看起来像https://radu-matei.com/blog/aspnet-core-websockets-middleware/ ),但是我遇到一个问题,例如我的客户端与之通信通过http协议的websocket,换句话说,websocket被包装在http中,当我的客户端发送http请求时,它转到服务方法,在此它最终使用websocket发送消息。 问题:是否可以在返回响应之前获得Websocket的消息回复?
public async Task SendMessage(WebSocket socket, string response)
{
if (socket.State != WebSocketState.Open)
return;
await socket.SendAsync(buffer: new ArraySegment<byte>(array: Encoding.UTF8.GetBytes(response),
offset: 0,
count: response.Length),
messageType: WebSocketMessageType.Text,
endOfMessage: true,
cancellationToken: CancellationToken.None);
await socket.ReceiveAsync() // When I finally receive some reply message, stop listening and return http response to my client
}
根据您所说的,我了解到您需要在多个服务之间共享websocket,每个服务都使用它的合适方式。基于这种情况,建议的实现具有一个Middleware
,其中包含需要socket的不同服务。 在决定哪个服务进行写和哪个reading-writing
时必须特别小心.websocket在同时reading-writing
的情况下是线程安全的,但在其他情况下则不是(线程writing-writing
, reading-reading
)
启动
public void ConfigureServices(IServiceCollection colllection){
collection.AddSingleton<Sender>();
collection.AddSingleton<Receiver>();
}
public void Configure(IApplicationBuilder app)
{
app.UseWebsockets();
app.UseMiddleware<DispatcherMiddleware>(); //receives socket here
}
中间件
public class DispatcherMiddleware{
private Sender sender;
private Receiver receiver;
private RequestDelegate next;
public Dispatcher(RequestDelegate req,Sender _sender,Receiver _receiver)
{
this.sender=_sender;
this.receiver=_receiver;
this.next=req;
}
public async Task Invoke(HttpContext context){
if(!context.WebSockets.IsWebSocketRequest){
return;
}
await DispatchAsync(context.WebSockets);
}
public async Task DispatchAsync(WebsocketManager manager){
WebSocket socket=await manager.AcceptWebSocketAsync();
Task t1=Task.Run(async()=>await this.sender.SendAsync(socket));
Task t2=Task.Run(async()=>await this.receiver.ReceiveAsync(socket));
await Task.WhenAll(t1,t2);
}
}
Websocket服务 (示例)
public class Sender(){
public async Task SendAsync(WebSocket ws){
try{
while(true){
// ws.SendAsync()
}
}
catch(Exception ex){
}
}
}
public class Receiver{
public async Task ReceiveAsync(WebSocket ws){
try
{
while(true){
//ws.ReceiveAsync(buffer)
}
}
catch (System.Exception)
{
throw;
}
}
}
编辑
您不能在同一套接字上执行并发读取/写入操作。这样说,如果您想使用同一套接字,可以做的是使其成为thread-safe
。由于该操作是async
我建议使用SemaphoreSlim
类。
以下是shared
套接字的实现:
public class SafeSocket {
private const int BUFFER_SIZE = 1024;
private WebSocket socket { get; set; }
private SemaphoreSlim @lock = new SemaphoreSlim(1);
public SafeSocket(WebSocket socket) {
this.socket = socket;
}
public async Task<byte[]> ReadAsync() {
byte[] buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
await @lock.WaitAsync();
try {
await this.socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
return buffer;
} catch (Exception) {
throw;
} finally {
@lock.Release();
}
}
}
public class DispatcherMiddleware {
private List<Sender> senders;
private Receiver receiver;
private RequestDelegate next;
public DispatcherMiddleware(RequestDelegate req, List<Sender> _senders, Receiver _receiver) {
this.senders = _senders;
this.receiver = _receiver;
this.next = req;
}
public async Task Invoke(HttpContext context) {
if (!context.WebSockets.IsWebSocketRequest) {
return;
}
await DispatchAsync(context.WebSockets);
}
public async Task DispatchAsync(WebSocketManager manager) {
WebSocket socket = await manager.AcceptWebSocketAsync();
SafeSocket commonSocket = new SafeSocket(socket);
Task[] senderTasks = new Task[this.senders.Count];
for (int senderIndex = 0; senderIndex < senderTasks.Length; senderIndex++) {
int index = senderIndex;// careful at index ! , make copy and use it inside closure !
senderTasks[senderIndex] = Task.Run(async () => {
await commonSocket.ReadAsync();
});
}
}
请记住,消息顺序不会被保留,同样可以应用于接收者。
所以最终您将得到N
发送者和K
接收者,即时间T
:
1
发件人将写信 1
接收器将读取 N-1
发件人将等待lock
K-1
接收器将等待lock
因此,最终在任何给定时间只有2
操作。 我不知道这是否是您所需要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.