인디노트

C# Non-ActiveX를 위한 Web Socket Server 만들기-vtortola 이용 본문

소스 팁/C, C++, C#

C# Non-ActiveX를 위한 Web Socket Server 만들기-vtortola 이용

인디개발자 2017. 11. 27. 16:38

ActiveX 를 사용한 프로그램은 사라져야 합니다.
하지만, 웹 만으로는 통제하지 못하는 경우가 있기 때문에 실행파일(exe)이 필요한 경우가 있는데요, 이 때에, 로컬 머신에서 실행되는 프로그램과 웹 간의 통신이 필요하게 됩니다.
이미지 출처 : html5korea.com

헷갈리면 안 될 게, 웹 소켓 코딩의 "프로그램과 웹 간의 통신" 이라는 말에서 "웹"은 말 자체로는 서버가 될 것 같지만 웹이지 브라우저에서 실행되므로 클라이언트가 됩니다. 그러므로 exe 에 웹소켓 서버가 내장 되어야 합니다.

이러한 부분에 있어서 TcpClient 등의 기본 클래스로 구현하고자 하였으나, 아무래도 직접 구현해 보니 처음에 성공했지만, Windows 7 에서는 구동이 안 되는 등 문제가 발생해서, 다른 오픈 소스 라이브러리를 사용하기로 했습니다.

바로, vtortola 라는.(어떻게 읽지요?)

NuGet Gallery : vtortola.WebSocketListener

패키지 설치하고, 아래와 같이 코딩하면 WebSocket 서버가 완성 됩니다. 포트 번호는 1818로 맞췄습니다. 포트 충돌이 나면, 코드 예제의 1818을 다른 값으로 바꾸면 됩니다.

C# 코드 입니다.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using vtortola.WebSockets;

namespace WebSocketServerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cancellation = new CancellationTokenSource();

            //var endpoint = new IPEndPoint(IPAddress.Any, 1818);
            var endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1818);
            WebSocketListener server = new WebSocketListener(endpoint);
            var rfc6455 = new vtortola.WebSockets.Rfc6455.WebSocketFactoryRfc6455(server);
            server.Standards.RegisterStandard(rfc6455);
            server.Start();

            Console.WriteLine("Echo Server started at " + endpoint.ToString());

            var task = Task.Run(() => AcceptWebSocketClientsAsync(server, cancellation.Token));

            Console.ReadKey(true);
            Console.WriteLine("Server stoping");
            cancellation.Cancel();
            task.Wait();
            Console.ReadKey(true);
        }

        //웹 소켓으로 접속하는 클라이언트를 받아들입니다.
        static async Task AcceptWebSocketClientsAsync(WebSocketListener server, CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                try
                {
                    //클라이언트가 들어왔을까요?
                    var ws = await server.AcceptWebSocketAsync(token).ConfigureAwait(false);

                    //소켓이 null 이 아니면, 핸들러를 스타트 합니다.(또 다른 친구가 들어올 수도 있으니 비동기로...)
                    if (ws != null)
                        Task.Run(() => HandleConnectionAsync(ws, token));
                }
                catch (Exception aex)
                {
                    Console.WriteLine("Error Accepting clients: " + aex.GetBaseException().Message);
                }
            }
            Console.WriteLine("Server Stop accepting clients");
        }

        static async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancellation)
        {
            try
            {
                //연결이 끊기지 않았고, 캔슬이 들어오지 않는 한 루프를 돕니다.
                while (ws.IsConnected && !cancellation.IsCancellationRequested)
                {
                    //클라이언트로부터 메시지가 왔는지 비동기로 읽어요.
                    String msg = await ws.ReadStringAsync(cancellation).ConfigureAwait(false);

                    //읽은 메시지가 null 이 아니면, 뭔가를 처리 합니다. 이 코드는 그냥 ack 라고 에코를 보내도록 만들었습니다.
                    if (msg != null)
                        ws.WriteString("ack : " + msg);
                }
            }
            catch (Exception aex)
            {
                Console.WriteLine("Error Handling connection: " + aex.GetBaseException().Message);
                try { ws.Close(); }
                catch { }
            }
            finally
            {
                //소켓은 Dispose 해 줍니다.
                ws.Dispose();
            }
        }
    }
}

다음은 테스트 할 수 있는 웹 페이지(HTML) 입니다.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        var socket = new WebSocket('ws://127.0.0.1:1818');
        socket.onopen = function() {
            alert('handshake successfully established. May send data now...');
            this.send('hello!!!')
        };
        socket.onmessage = function (event) {
            console.log(event.data);
        };
        socket.onclose = function() {
            alert('connection closed');
        };
    </script>
</head>
<body>
</body>
</html>

ActiveX를 벗어나기 위한 조치는 이것 말고도 사실 여러가지 더 있습니다. 바로 웹 페이지 상에서 프로토콜 콜을 이용한 프로그램 호출인데요. 이 부분 또한 구현한 바 있습니다만, 여러가지 복잡한 작업을 해야 하기 때문에(레지스트리를 건드리는 건 기본이고, 브라우저 경고창 제거를 위하여 크롬의 경우 설정 파일을 건드려야 하는 등) 블로그에서 따로 설명은 하지 않으려 합니다.

아, 물론, 위에 작성한 웹 소켓 프로그램을 윈도우 서비스로 녹여 넣는다면, 프로토콜 콜이 없어도 되긴 하겠네요. (그래도 설치는 필요 하죠.)

오랜만에 글을 썼네요.


반응형

'소스 팁 > C, C++, C#' 카테고리의 다른 글

서비스에서 GUI 창 띄우기  (0) 2018.01.09
Dokan Directory Structure  (0) 2018.01.04
BSTR 변환  (0) 2017.12.29
OpenSSL 컴파일(compile) & 빌드(build)  (0) 2017.12.21
File System Filter Driver Tutorial  (0) 2017.12.06
Comments