C# 如何在asp net核心中的服务之间共享websocket
我正在使用中间件在.NETCore上设置websocket(看起来是这样的),但我有一个问题 例如,我的客户机通过http协议与websocket通信,换句话说,websocket是用http包装的,当我的客户机发送http请求时,它会转到服务方法,最后使用websocket发送消息。 问题:是否有可能在返回响应之前获得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
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();
});
}
}