Вступление
Я думаю, что многие веб-разработчики задают себе вопрос о том, как передать пользователю какое-либо сообщение, напоминание. Раньше для этого было необходимо постоянно отправлять запросы к веб-серверу, но теперь появилась такая удобная технология, как Web Socket.
В этой статье я хочу показать, как можно написать простой чат на ASP.NET MVC 4 с помощью Web Socket.
Приступаем
Для начала нам необходимо создать пустой проект ASP.NET MVC 4

И добавить в него универсальный обработчик ChatHandler

Теперь изменим код метода ProcessRequest нашего обработчика на
public void ProcessRequest(HttpContext context) { //Если запрос является запросом веб сокета if (context.IsWebSocketRequest) context.AcceptWebSocketRequest(WebSocketRequest); }
Добавим 2 переменные
// Список всех клиентов private static readonly List<WebSocket> Clients = new List<WebSocket>(); // Блокировка для обеспечения потокабезопасности private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
И напишем основной метод нашего чата
private async Task WebSocketRequest(AspNetWebSocketContext context) { // Получаем сокет клиента из контекста запроса var socket = context.WebSocket; // Добавляем его в список клиентов Locker.EnterWriteLock(); try { Clients.Add(socket); } finally { Locker.ExitWriteLock(); } // Слушаем его while (true) { var buffer = new ArraySegment<byte>(new byte[1024]); // Ожидаем данные от него var result = await socket.ReceiveAsync(buffer, CancellationToken.None); //Передаём сообщение всем клиентам for (int i = 0; i < Clients.Count; i++) { WebSocket client = Clients[i]; try { if (client.State == WebSocketState.Open) { await client.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); } } catch (ObjectDisposedException) { Locker.EnterWriteLock(); try { Clients.Remove(client); i--; } finally { Locker.ExitWriteLock(); } } } } }
Теперь наш обработчик должен выглядеть так:
Скрытый текст
public class ChatHandler : IHttpHandler { // Список всех клиентов private static readonly List<WebSocket> Clients = new List<WebSocket>(); // Блокировка для обеспечения потокабезопасности private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); public void ProcessRequest(HttpContext context) { //Если запрос является запросом веб сокета if (context.IsWebSocketRequest) context.AcceptWebSocketRequest(WebSocketRequest); } private async Task WebSocketRequest(AspNetWebSocketContext context) { // Получаем сокет клиента из контекста запроса var socket = context.WebSocket; // Добавляем его в список клиентов Locker.EnterWriteLock(); try { Clients.Add(socket); } finally { Locker.ExitWriteLock(); } // Слушаем его while (true) { var buffer = new ArraySegment<byte>(new byte[1024]); // Ожидаем данные от него var result = await socket.ReceiveAsync(buffer, CancellationToken.None); //Передаём сообщение всем клиентам for (int i = 0; i < Clients.Count; i++) { WebSocket client = Clients[i]; try { if (client.State == WebSocketState.Open) { await client.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); } } catch (ObjectDisposedException) { Locker.EnterWriteLock(); try { Clients.Remove(client); i--; } finally { Locker.ExitWriteLock(); } } } } } public bool IsReusable { get { return false; } } }
Теперь мы можем написать простенькую html страничку, которая будет содержать Web Socket, подключающийся к нашему обработчику
<!DOCTYPE html> <html> <head> <title>Простой чат</title> </head> <body> <input type="text" id="user" /> <input type="text" id="message" /> <input type="button" value="send" id="send" /> <div id='messages'></div> <script type="text/javascript"> var socket, $txt = document.getElementById('message'), $user = document.getElementById('user'), $messages = document.getElementById('messages'); if (typeof (WebSocket) !== 'undefined') { socket = new WebSocket("ws://localhost/SimpleChatApplication/ChatHandler.ashx"); } else { socket = new MozWebSocket("ws://localhost/SimpleChatApplication/ChatHandler.ashx"); } socket.onmessage = function (msg) { var $el = document.createElement('p'); $el.innerHTML = msg.data; $messages.appendChild($el); }; socket.onclose = function (event) { alert('Мы потеряли её. Пожалуйста, обновите страницу'); }; document.getElementById('send').onclick = function () { socket.send($user.value + ' : ' + $txt.value); $txt.value = ''; }; </script> </body> </html>
Теперь мы можем запустить наш чат, но вот только свойство context.IsWebSocketRequest будет всегда false.
Это происходит из-за того, что Visual Studio по умолчанию для отладки использует IIS Express, который не поддерживает Web Socket. Для того, чтобы наконец поиграться с сокетами нам необходимо установить IIS и настроить Visual Studio для работы с ним по умолчанию.
Для этого выполним несколько шагов:
Шаг 1. Устанавливаем IIS.
Для этого перейдём в Панель управления -> Программы -> Включение и отключение компонентов Windows
Там найдём службы IIS и поставим на них галку

Шаг 2. Настраиваем IIS.
По умолчанию компонент Web Socket в IIS отключён. Чтобы его включить в компонентах Windows пройдём по пути Службы IIS -> Службы Интернета -> Компоненты разработки приложений. Там найдём Протокол WebSocket и поставим на нём галку

Шаг 3. Настраиваем Visual Studio.
Чтобы заставить VS использовать IIS для отладки, нам необходимо зайти в свойства проекта -> Веб и там отжать галку Использовать IIS Express. (Если галка у вас не активна, перезапустите студию)

Завершение
Теперь мы можем смело запустить наш проект и поприветствовать самого себя!

Скачать файл проекта можно здесь.