하지만, 웹 만으로는 통제하지 못하는 경우가 있기 때문에 실행파일(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를 벗어나기 위한 조치는 이것 말고도 사실 여러가지 더 있습니다. 바로 웹 페이지 상에서 프로토콜 콜을 이용한 프로그램 호출인데요. 이 부분 또한 구현한 바 있습니다만, 여러가지 복잡한 작업을 해야 하기 때문에(레지스트리를 건드리는 건 기본이고, 브라우저 경고창 제거를 위하여 크롬의 경우 설정 파일을 건드려야 하는 등) 블로그에서 따로 설명은 하지 않으려 합니다.
아, 물론, 위에 작성한 웹 소켓 프로그램을 윈도우 서비스로 녹여 넣는다면, 프로토콜 콜이 없어도 되긴 하겠네요. (그래도 설치는 필요 하죠.)
오랜만에 글을 썼네요.