Pull to refresh

Делаем чат на ASP.NET с помощью Web Socket

Reading time4 min
Views80K

Вступление



Я думаю, что многие веб-разработчики задают себе вопрос о том, как передать пользователю какое-либо сообщение, напоминание. Раньше для этого было необходимо постоянно отправлять запросы к веб-серверу, но теперь появилась такая удобная технология, как 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. (Если галка у вас не активна, перезапустите студию)



Завершение


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



Скачать файл проекта можно здесь.
Tags:
Hubs:
+24
Comments26

Articles