Akashic Records

First WebSocket 본문

오래된글/Articles

First WebSocket

Andrew's Akashic Records 2018. 5. 3. 16:12
728x90

HTML5의 주 기능 중에  “Websocket”이 있다는 건 웬만한 개발자라면 다 아는 사실이다. 하지만 사실상 적용된 사례는 드문 것 같다. 이유야 여러가지가 있겠지만 환경 부족과 개발자 인식의 부족인 문제인 것 같다. 또한 최근에는 모든 것이 다 되는(?) 모바일에 그 필요성이 없다고 생각 할 수도 있겠다.

“Websocket”의 특징을 말한다면 “Web브라우저를 기반으로 서버에서 클라이언트로 돈 안들이고 Message을 보낼수 있는 유일한 기술”라고 할 수 있을 것이다.


WebSocket개발에는 뭐가 필요할까?

WebSocket을 http”//로 시작하지 않고 ws://로 시작하는 URL형태를 가진다. 이는 HTTP 프로토콜을 기반으로 확장된 별도의 프로토콜을 가지기 때문이다.

“JSR 356(JavaTM API for WebSocket)”으로 명명된  WebSocket을 지원하는 WAS는 따로있으며

Tomcat의 경우 7이상, Jetty는 9이상의 버전부터 지원하고 있다.(다른 건 잘 모르겠습니다...)


Tomcat 7의 example로 포함된 WebSocket 예를 살펴보면


Server Side: EchoMessage

package websocket.echo;


import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.CharBuffer;

import java.util.Date;


import javax.servlet.Servlet;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServletRequest;


import org.apache.catalina.websocket.MessageInbound;

import org.apache.catalina.websocket.StreamInbound;

import org.apache.catalina.websocket.WebSocketServlet;

import org.apache.catalina.websocket.WsOutbound;


@WebServlet("/echo")

public class EchoMessage extends WebSocketServlet implements Servlet {


private static final long serialVersionUID = 1L;

private volatile int byteBufSize;

private volatile int charBufSize;


@Override

public void init() throws ServletException {

super.init();

byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);

charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);

}


public int getInitParameterIntValue(String name, int defaultValue) {

String val = this.getInitParameter(name);

int result;

if (null != val) {

try {

result = Integer.parseInt(val);

} catch (Exception x) {

result = defaultValue;

}

} else {

result = defaultValue;

}


return result;

}


@Override

protected StreamInbound createWebSocketInbound(String subProtocol,

HttpServletRequest request) {

return new EchoMessageInbound(byteBufSize, charBufSize);

}


private static final class EchoMessageInbound extends MessageInbound {


public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {

super();

setByteBufferMaxSize(byteBufferMaxSize);

setCharBufferMaxSize(charBufferMaxSize);

}


@Override

protected void onBinaryMessage(ByteBuffer message) throws IOException {

getWsOutbound().writeBinaryMessage(message);

}


@Override

protected void onTextMessage(CharBuffer message) throws IOException {

System.out.println("From Client:" + message.toString());

getWsOutbound().writeTextMessage(message);

}


@Override

protected void onClose(int status) {

super.onClose(status);

System.out.println("종료:" + status);

}


@Override

protected void onOpen(WsOutbound outbound) {

super.onOpen(outbound);

System.out.println("연결:" + outbound);

CharBuffer buffer = CharBuffer.allocate(100);

try {

String string = "Current time:"+ new Date()+", WebSocket 연결 하였습니다.";


for (int i = 0; i < string.length(); i++) {

buffer.put(string.charAt(i));

}


buffer.flip();

outbound.writeTextMessage(buffer);

buffer.clear();

} catch (IOException e) {

e.printStackTrace();

}

}


}


}

onOpen, onClose 콜백 메서드을 오버라이드해서 적절하게 사용 가능합니다.(여기서는 로그만..)

Inbound 타입은 여러가지가 있어 적절하게 골라 사용하면 됩니다.


Client Side: echo.html

<!DOCTYPE html>

<html>

<head>

   <title>Apache Tomcat WebSocket Examples: Echo</title>

   <style type="text/css">

       #connect-container {

           float: left;

           width: 400px

       }


       #connect-container div {

           padding: 5px;

       }


       #console-container {

           float: left;

           margin-left: 15px;

           width: 400px;

       }


       #console {

           border: 1px solid #CCCCCC;

           border-right-color: #999999;

           border-bottom-color: #999999;

           height: 170px;

           overflow-y: scroll;

           padding: 5px;

           width: 100%;

       }


       #console p {

           padding: 0;

           margin: 0;

       }

   </style>

   <script type="text/javascript">

       var ws = null;


       function setConnected(connected) {

           document.getElementById('connect').disabled = connected;

           document.getElementById('disconnect').disabled = !connected;

           document.getElementById('echo').disabled = !connected;

       }


       function connect() {

           var target = document.getElementById('target').value;

           if (target == '') {

               alert('Please select server side connection implementation.');

               return;

           }

           if ('WebSocket' in window) {

               ws = new WebSocket(target);

           } else if ('MozWebSocket' in window) {

               ws = new MozWebSocket(target);

           } else {

               alert('WebSocket is not supported by this browser.');

               return;

           }

           ws.onopen = function () {

               setConnected(true);

               log('Info: WebSocket connection opened.');

           };

           ws.onmessage = function (event) {

               log('Received: ' + event.data);

           };

           ws.onclose = function () {

               setConnected(false);

               log('Info: WebSocket connection closed.');

           };

       }


       function disconnect() {

           if (ws != null) {

               ws.close();

               ws = null;

           }

           setConnected(false);

       }


       function echo() {

           if (ws != null) {

               var message = document.getElementById('message').value;

               log('Sent: ' + message);

               ws.send(message);

           } else {

               alert('WebSocket connection not established, please connect.');

           }

       }


       function updateTarget(target) {

           if (window.location.protocol == 'http:') {

               document.getElementById('target').value = 'ws://' + window.location.host + target;

           } else {

               document.getElementById('target').value = 'wss://' + window.location.host + target;

           }

       }


       function log(message) {

           var console = document.getElementById('console');

           var p = document.createElement('p');

           p.style.wordWrap = 'break-word';

           p.appendChild(document.createTextNode(message));

           console.appendChild(p);

           while (console.childNodes.length > 25) {

               console.removeChild(console.firstChild);

           }

           console.scrollTop = console.scrollHeight;

       }

   </script>

</head>

<body>

<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable

   Javascript and reload this page!</h2></noscript>

<div>

   <div id="connect-container">

       <div>

           <span>Connect using:</span>

           <!-- echo example using streams on the server side -->

           <input id="radio1" type="radio" name="group1" value="/FirstWebSocket/websocket/echoStream"

                  onclick="updateTarget(this.value);"> <label for="radio1">streams</label>

           <!-- echo example using messages on the server side -->

           <input id="radio2" type="radio" name="group1" value="/FirstWebSocket/websocket/echoMessage"

                  onclick="updateTarget(this.value);"> <label for="radio2">messages</label>

       </div>

       <div>

           <input id="target" type="text" size="40" style="width: 350px"/>

       </div>

       <div>

           <button id="connect" onclick="connect();">Connect</button>

           <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>

       </div>

       <div>

           <textarea id="message" style="width: 350px">Here is a message!</textarea>

       </div>

       <div>

           <button id="echo" onclick="echo();" disabled="disabled">Echo message</button>

       </div>

   </div>

   <div id="console-container">

       <div id="console"></div>

   </div>

</div>

</body>

</html>


기타 배포에 필요한 Code

EchoStream.java

package websocket.echo;


import java.io.IOException;

import java.io.InputStream;

import java.io.Reader;


import javax.servlet.http.HttpServletRequest;


import org.apache.catalina.websocket.StreamInbound;

import org.apache.catalina.websocket.WebSocketServlet;

import org.apache.catalina.websocket.WsOutbound;


public class EchoStream extends WebSocketServlet {


   private static final long serialVersionUID = 1L;


   @Override

   protected StreamInbound createWebSocketInbound(String subProtocol,

           HttpServletRequest request) {

       return new EchoStreamInbound();

   }


   private static final class EchoStreamInbound extends StreamInbound {


       @Override

       protected void onBinaryData(InputStream is) throws IOException {

           // Simply echo the data to back to the client.

           WsOutbound outbound = getWsOutbound();


           int i = is.read();

           while (i != -1) {

               outbound.writeBinaryData(i);

               i = is.read();

           }


           outbound.flush();

       }


       @Override

       protected void onTextData(Reader r) throws IOException {

           // Simply echo the data to back to the client.

           WsOutbound outbound = getWsOutbound();


           int c = r.read();

           while (c != -1) {

               outbound.writeTextData((char) c);

               c = r.read();

           }


           outbound.flush();

       }

   }

}


Web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

version="3.0">

<display-name>FirstWebSocket</display-name>

<servlet>

<servlet-name>wsEchoMessage</servlet-name>

<servlet-class>websocket.echo.EchoMessage</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>wsEchoMessage</servlet-name>

<url-pattern>/websocket/echoMessage</url-pattern>

</servlet-mapping>


<servlet>

<servlet-name>wsEchoStream</servlet-name>

<servlet-class>websocket.echo.EchoStream</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>wsEchoStream</servlet-name>

<url-pattern>/websocket/echoStream</url-pattern>

</servlet-mapping>

</web-app>



결론

만일 여러분이 Dashboard 형태의 Web Application을 만든다면 “WebSocket”은 강력한 도구가 될 것입니다.

그 외 브라우저 환경에서 알림을 받는다든지, 웹 화면 Refresh 없이 데이터 변경을 위한 모든 상황에도 활용이 가능할 것입니다.



728x90
Comments