어플리케이션에 Servlet Container 이외도 여러가지 서버 기능을 내장할 수 있는 jetty 라이브러리를 이용해 WebSocket 서버를 구현하는 코드에 대한 뼈대를 정리한다. WebSocket는 HTML5에서 지원하는 기능으로 서버와 클라이언트는 연결되어 있는 상태에서 상호간에 데이터를 주고니 받거니 할 수 있는 기술이다. WebSocket은 그 막강한 통신 기능에 비해 서버와 클라이언트의 코드가 매우 작다. 그래서인지 뭔가 부족한 느낌이 드는데, 일단 기본적인거 정리하고 부족한 느낌이 드는 이유는 나중에 파악해 보자.
먼저 서버 단의 코드이다. Server를 구동하는 클래스와 WebSocket를 통해 접속하는 클라이언트를 나타내는 Session 범위의 클래스인데, 먼저 Server를 구동하는 클래스는 아래와 같다.
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.websocket.server.WebSocketHandler; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; public class test_webSocket { public static void main(String[] args) { Server server = new Server(8080); WebSocketHandler wsHandler = new WebSocketHandler() { @Override public void configure(WebSocketServletFactory factory) { factory.register(MyEchoSocket.class); }; }; ContextHandler context = new ContextHandler(); context.setContextPath("/echoServer"); context.setHandler(wsHandler); server.setHandler(context); try { server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } }
그리고 다음은 서버에 접속하는 클라이언트(Session 객체로 구분) 마다 생성되는 Echo 기능을 갖는 MyEchoSocket 클래스이다.
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketListener; public class MyEchoSocket implements WebSocketListener { private Session outbound; public MyEchoSocket() { System.out.println("class loaded " + this.getClass()); } @Override public void onWebSocketConnect(Session session) { this.outbound = session; } @Override public void onWebSocketError(Throwable cause) { cause.printStackTrace(System.err); } @Override public void onWebSocketText(String message) { if ((outbound != null) && (outbound.isOpen())) { String echoMessage = outbound.hashCode() + " : " + message; outbound.getRemote().sendString(echoMessage, null); } } @Override public void onWebSocketBinary(byte[] payload, int offset, int len) { //. } @Override public void onWebSocketClose(int statusCode, String reason) { System.out.println("Close, statusCode = " + statusCode + ", reasone = " + reason); this.outbound = null; } }
요즘 웹이 그렇듯이 WebSocket 기술은 Binary 데이터도 주고 받을 수 있다는 것을 알 수 있다. 중요한 것은 클라이언트 하나가 접속을 하면 위의 클래스가 매번 생성된다는 것이고, 클라이언트는 Session 객체를 통해 나타내지며, 이 객체를 통해 데이터를 주고 받을 수 있다.
서버를 살펴봤으니, 이제 클라이언트 코드를 살펴보면..
위의 13번 부분에 들어가는 js 코드는 다음과 같다.
var webSocket = new WebSocket("ws://localhost:8080/echoServer/"); var msgField = document.getElementById("messageField"); var divMsg = document.getElementById("msg-box"); function sendMsg() { var msgToSend = msgField.value; webSocket.send(msgToSend); divMsg.innerHTML += "Client> " + msgToSend + ""; msgField.value = ""; } webSocket.onmessage = function (message) { divMsg.innerHTML += "Server> : " + message.data; } webSocket.onopen = function () { alert("connection opened"); } webSocket.onclose = function () { alert("connection closed"); } webSocket.onerror = function (message) { alert("error: " + message); }
서버를 기동한 뒤, 클라이언트를 실행하고 메세지를 날리면, 해당 메세지가 메아리로 돌아오는 것을 볼 수 있다.