C# 如何在asp net核心中的服务之间共享websocket

C# 如何在asp net核心中的服务之间共享websocket,c#,asp.net-core,websocket,.net-core,middleware,C#,Asp.net Core,Websocket,.net Core,Middleware,我正在使用中间件在.NETCore上设置websocket(看起来是这样的),但我有一个问题 例如,我的客户机通过http协议与websocket通信,换句话说,websocket是用http包装的,当我的客户机发送http请求时,它会转到服务方法,最后使用websocket发送消息。 问题:是否有可能在返回响应之前获得websocket对消息的回复 public async Task SendMessage(WebSocket socket, string response) { if

我正在使用中间件在.NETCore上设置websocket(看起来是这样的),但我有一个问题 例如,我的客户机通过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
}
公共异步任务SendMessage(WebSocket套接字,字符串响应)
{
if(socket.State!=WebSocketState.Open)
返回;
wait socket.SendAsync(缓冲区:new ArraySegment(数组:Encoding.UTF8.GetBytes(响应)),
偏移量:0,
计数:响应。长度),
messageType:WebSocketMessageType.Text,
endOfMessage:对,
cancellationToken:cancellationToken.None);
wait socket.ReceiveAsync()//当我最终收到一些回复消息时,停止侦听并将http响应返回给我的客户端
}

根据您所说的,我理解您需要在多个服务之间共享websocket,每个服务按照自己认为合适的方式使用websocket。基于此场景,建议的实现有一个
中间件
,其中包含需要套接字的不同服务。在决定哪个服务执行写入操作,哪个服务执行读取操作时,必须特别小心。websocket在
读写
的同时是线程安全的,但在其他情况下则不是。(
写写

启动

public void ConfigureServices(IServiceCollection colllection){
     collection.AddSingleton<Sender>();
     collection.AddSingleton<Receiver>();
 }
 public void Configure(IApplicationBuilder app)
 {
    app.UseWebsockets();
    app.UseMiddleware<DispatcherMiddleware>(); //receives socket here

 }
Websocket服务(示例)

编辑
你不能在同一个套接字上执行并发读/写操作。如果你想使用同一个套接字,你可以做的就是使它成为线程安全的。由于操作是异步的,我建议使用
信号量lim

下面是
共享
套接字的实现:

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();
                });
            }
        }
公共类安全套接字{
private const int BUFFER_SIZE=1024;
私有WebSocket套接字{get;set;}
私有信号量lim@lock=新信号量lim(1);
公共安全插座(WebSocket插座){
this.socket=socket;
}
公共异步任务ReadAsync(){
字节[]缓冲区=ArrayPool.Shared.Rent(缓冲区大小);
wait@lock.WaitAsync();
试一试{
等待this.socket.SendAsync(缓冲区,WebSocketMessageType.Text,true,CancellationToken.None);
返回缓冲区;
}捕获(例外){
投掷;
}最后{
@锁定。释放();
}
}
}
公共类DispatcherMiddle软件{
私人名单发送者;
专用接收机;
其次是私人委托;
公共DispatcherMiddle软件(请求委托请求、列表发送者、接收者和接收者){
this.senders=\u senders;
this.receiver=\u receiver;
this.next=req;
}
公共异步任务调用(HttpContext上下文){
如果(!context.WebSockets.IsWebSocketRequest){
返回;
}
wait DispatchAsync(context.WebSockets);
}
公共异步任务分派异步(WebSocketManager){
WebSocket套接字=等待管理器。AcceptWebSocketAsync();
SafeSocket commonSocket=新的SafeSocket(插座);
Task[]senderTasks=新任务[this.senders.Count];
对于(int-senderIndex=0;senderIndex{
等待commonSocket.ReadAsync();
});
}
}
请记住,消息顺序不会被保留。这同样适用于接收者。
因此,您最终得到的是
N
发送方和
K
接收方,在
T
时:

  • 1
    发送方将写入
  • 1
    接收器将读取
  • N-1
    发送方将等待
    锁定
  • K-1
    接收器将等待
    锁定
  • 因此,在任何给定的时间,最终只会有
    2个
    操作。
    
    我不知道这是否是您所需要的。

    您基本上希望读取初始http请求的标题,然后将协议切换到
    ws
    ?我想我想要的不是那么简单,我有一个中间件正在侦听消息,但我不仅希望在中间件中侦听,还希望在http时在服务方法中侦听请求来了,他转到控制器->服务,所以我想在这里开始监听以前在中间件中初始化过的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;
            }
    
        }
    }
    
    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();
                    });
                }
            }