Я очень близок к внедрению системы на C#, которая позволяет мне отправлять уведомления pu sh нажатием кнопки с интерфейса, написанного на ASP. net, на * 1066. * консольное приложение, действующее как клиент. Все это с использованием WebSockets.
После прочтения большого количества руководств и повторного использования кода, найденного в Интернете, я уже смог успешно установить sh соединение WebSocket. Я пока не могу отправить уведомление.
Часть, с которой я борюсь, - это функция, которая запускается при нажатии кнопки:
//Close ticket and send push-notification over websocket
public void Close(int id) {
//Ticket ticket = mgr.GetTicket(id);
//Create a new notification
Notification notif = new Notification();
notif.message = "Rofl test123 Notification lol";
//Initialize WebSocketMiddleware here??
//WebSocketsMiddleware wsm = new WebSocketsMiddleware(what parameter??);
//wsm.Invoke(what HttpContext parameter???)
NotificationManager notifMgr;
//notifMgr.AddSubscriber(wsm);
//notifMgr.SendNotificationAsync(notif);
return;
}
Указание c вопросов / проблем, с которыми я сталкиваюсь:
- Как инициализировать класс WebSocketsMiddleware? Нужно ли его инициализировать, если да, каков параметр с типом RequestDelegate? Что мне передать этому параметру?
- WebSocketsMiddleware имеет функцию Invoke с контекстом параметра типа HttpContext. Мне просто нужно передать
new HttpContext()
на это? Этого достаточно? - Кто-то создал класс NotificationManager, этот класс использует промежуточное ПО для фактической отправки уведомления. Нужно ли просто передавать инициализированную переменную WebSocketsMiddleware в качестве параметра для NotificationManager.AddSubscriber ()? Будут ли уведомления каждого клиента красиво разделены?
- Можно ли после этого просто использовать SendNotificationAsyn c () для отправки уведомления?
- Бонусный вопрос: скажем, что у каждого клиента есть свой кнопка. Когда я нажимаю кнопку клиента, только этот клиент может получить уведомление pu sh. Как убедиться, что все остальные клиенты также не получают одинаковое уведомление?
Чтобы помочь мне с этими вопросами, вам понадобятся следующие классы. Вопрос только о WebSockets, но больше о том, как инициировать и использовать классы, которые я собрал из учебных пособий.
Notification.cs - Класс, представляющий Уведомление (текст уведомления, дата отправки, ... ):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SC.UI.MVC.Models
{
public class Notification
{
public Guid? notificationId { get; set; }
public int id { get; set; }
public DateTime timestamp { get; set; }
public string message { get; set; }
public string type { get; set; }
public Notification()
{
// add a new guid as a unique identifier for the notification in the db
notificationId = Guid.NewGuid();
}
}
}
WebSocketsMiddleware.cs - обрабатывает низкоуровневую часть WebSockets, вызывая соединение et c:
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace NotificationsApi.Notifications
{
public class WebSocketsMiddleware
{
// private variable to track the next delegate to call in the request chain
private readonly RequestDelegate _next;
public WebSocketsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
CancellationToken ct = context.RequestAborted;
string currentSubscriberId = null;
WebSocket currentSocket = null;
// we want to listen on a specific path for websocket communications
if (context.Request.Path == "/notifications/ws")
{
// make sure the request is a websocket request
if (context.WebSockets.IsWebSocketRequest)
{
currentSocket = await context.WebSockets.AcceptWebSocketAsync();
currentSubscriberId = NotificationManager.Instance.AddSubscriber(currentSocket);
// keep the socket open until we get a cancellation request
while (true)
{
if (ct.IsCancellationRequested)
{
break;
}
}
}
else // return an HTTP bad request status code if anything other a web socket request is made on this URI
{
context.Response.StatusCode = 400;
}
}
// clean up the socket
if (!string.IsNullOrWhiteSpace(currentSubscriberId))
{
NotificationManager.Instance.RemoveSubscriber(currentSubscriberId);
if (currentSocket != null)
{
await currentSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
currentSocket.Dispose();
}
}
// call the next delegate in the pipeline
await _next(context);
return;
}
}
}
NotificationManager.cs - интерфейс / класс с тремя функциями для добавления и удаления подписчиков, а также для фактической отправки уведомления. Для этого используется промежуточное программное обеспечение WebSocket:
using SC.UI.MVC.Models;
//using NotificationsApi.Persistence;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NotificationsApi.Notifications
{
// interface for NotificationManager for dependency injection
public interface INotificationManager
{
string AddSubscriber(WebSocket subscriber);
void RemoveSubscriber(string subscriberId);
Task SendNotificationAsync(Notification notification);
}
public class NotificationManager : INotificationManager
{
// static instance of the NotificationManager class
private static INotificationManager _instance;
public static INotificationManager Instance { get { return _instance ?? (_instance = new NotificationManager()); } set { _instance = value; } }
// static dictionary to keep track of all notification subscribers
private static ConcurrentDictionary<string, WebSocket> _subscribers = new ConcurrentDictionary<string, WebSocket>();
// adds a subscriber to receive notifications
public string AddSubscriber(WebSocket subscriber)
{
var subscriberId = Guid.NewGuid().ToString();
_subscribers.TryAdd(subscriberId, subscriber);
return subscriberId.ToString();
}
// removes a notifications subscriber
public void RemoveSubscriber(string subscriberId)
{
WebSocket empty;
_subscribers.TryRemove(subscriberId, out empty);
}
// sends a notification to all subscribers
public async Task SendNotificationAsync(Notification notification)
{
// add the notification to the persistence store
//await PersistenceManager.Instance.AddNotificationAsync(notification);
// send the notification to all subscribers
foreach (var s in _subscribers)
{
if (s.Value.State == WebSocketState.Open)
{
var jsonNotification = JsonConvert.SerializeObject(notification);
await SendStringAsync(s.Value, jsonNotification);
}
}
}
// sends a string via web socket communication
private async Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
{
var buffer = Encoding.UTF8.GetBytes(data);
var segment = new ArraySegment<byte>(buffer);
await socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
}
}
}
Client.cs - клиент, получающий уведомление pu sh. Я не думаю, что проблема здесь:
/* WEBSOCKET PART */
//Variables for websocket
private static object consoleLock = new object();
private const int sendChunkSize = 256;
private const int receiveChunkSize = 256;
private const bool verbose = true;
private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(30000);
//Function to check if a ticket from this client is closed/solved
public void checkTicketSolved() {
Thread.Sleep(1000);
Connect("ws://localhost:5050/notifications/ws").Wait();
Console.WriteLine("Press any key to exit...");
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Task.WhenAll(Receive(webSocket), Send(webSocket));
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex);
}
finally
{
if (webSocket != null)
webSocket.Dispose();
Console.WriteLine();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
static UTF8Encoding encoder = new UTF8Encoding();
private static async Task Send(ClientWebSocket webSocket)
{
//byte[] buffer = encoder.GetBytes("{\"op\":\"blocks_sub\"}"); //"{\"op\":\"unconfirmed_sub\"}");
byte[] buffer = encoder.GetBytes("{\"op\":\"unconfirmed_sub\"}");
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
while (webSocket.State == WebSocketState.Open)
{
LogStatus(false, buffer, buffer.Length);
await Task.Delay(delay);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
byte[] buffer = new byte[receiveChunkSize];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
LogStatus(true, buffer, result.Count);
}
}
}
private static void LogStatus(bool receiving, byte[] buffer, int length)
{
lock (consoleLock)
{
Console.ForegroundColor = receiving ? ConsoleColor.Green : ConsoleColor.Gray;
//Console.WriteLine("{0} ", receiving ? "Received" : "Sent");
if (verbose)
Console.WriteLine(encoder.GetString(buffer));
Console.ResetColor();
}
}
}
Вы также можете найти этот код на Github . Соответствующие части кода находятся в:
- WebServer / UI-MVC / Controllers / TicketController.cs -> Содержит функцию, запускаемую при нажатии кнопки.
- WebServer / UI -MVC / Уведомления -> Содержит NotificationManager.cs и WebSocketsMiddleware.cs
- WebServer / UI-MVC / Models -> Содержит Notification.cs
- Клиент / содержит весь код клиентского консольного приложения
Чтобы дать вам некоторый контекст о приложении: Это приложение представляет собой систему тикетов, которая позволяет клиентам / клиентам, использующим мое программное обеспечение, открывать тикеты поддержки. Часть WebServer предназначена для администраторов и сотрудников, которые отвечают за тикеты и управляют ими. Консольное приложение - это то, что нужно установить моим клиентам / клиентам, чтобы связаться со службой поддержки и открыть заявку в службу поддержки. Когда администратор закрывает заявку клиента, нажимая кнопку, это означает, что заявка и, таким образом, проблема клиента была решена и закрыта. В результате клиент получает уведомление о * pu sh.
Я не ищу ссылки на другие учебные пособия по WebSockets или предложения, использующие SignalR вместо этого или что-то подобное, я уже прочитал их все, и я Я уже использовал SignalR, но сейчас заинтересован в чистых WebSockets. Я был бы очень благодарен за кого-то, кто мог бы помочь мне разработать первую часть кода, размещенного в этом вопросе (функция Close), и объяснить, что он сделал. Спасибо!