HTML5 WebSocket with Tomcat 7
http://tomcat.apache.org/tomcat-7.0-doc/web-socket-howto.html
Tomcat 7에 포함된 WebSocket API (JSR-356) 예제를 참고하여 아래와 같은 예제를 작성하고 실행하고자 한다.
Eclipse의 Dynamic Web Project에서 테스트할 때는 실행되지 않았지만, Eclipse에서 컴파일된 파일을 톰캣에 배포한 후에 실행하면 문제가 없었다.
서버측에서 JSON 문자열을 처리하기 위해 사용한 라이브러리 https://code.google.com/p/json-simple/
위의 URL을 참조하거나 로컬시스템에 설치한 Tomcat 7 의 examples 디렉토리에서 WebSocket 예제를 찾을 수 있다.
다음 예제는 클라이언트측(Javascript)에서 WebSocket을 이용하여 서버측에 접속할 때 2가지 방식으로 데이터를 전달하는 예이다.
1. 파라미터를 요청 URL에 포함하여 전달하는 방법
2. 파라미터가 아닌 일반 데이터를 JSON 문자열로 전달하는 방법
서버에서 수신한 데이터는 다시 클라이언트 측으로 되돌려 보내는 Echo 서버의 기능이다
WebSocket Client Code (HTML5 Javascript)
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>WebSocket Client</title>
<script type="text/javascript">
var ws = null;
function connect() {
// 아래의 적색 경로는 서버측의 ServerEndPoint 를 사용해야 하고 ? 표시 오른쪽에는 파라미터가 온다
var target = "ws://localhost:8080/websocket/echoAnnotation?usr=홍길동"; //서버에서 파라미터를
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 () {
document.getElementById("msg").innerText = 'Info: WebSocket connection opened.';
};
ws.onmessage = function (event) {
document.getElementById("msg").innerText = 'Received: '+event.data;
};
ws.onclose = function () {
document.getElementById("msg").innerText = 'Info: WebSocket connection closed.';
};
}
function send() {// JSON 문자열을 서버로 전송한다
var msg = {
"usrName":"홍길동", "phone":"010-1234-6543", "content":"Hello World, 감사합니다"
};
var jsonStr = JSON.stringify(msg);
ws.send(jsonStr);
}
function sendFile() {
// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
ws.send(file);
}
</script>
</head>
<body>
<input type="button" value="Connect" onClick="javascript:connect();"><br/>
<input type="button" value="Send" onClick="javascript:send();"><br/><p/>
파일선택<input type="file" id="file1"><p/>
<input type="button" value="sendFile" onClick="javascript:sendFile();"><p/>
<div id="msg"></div>
</body>
</html>
WebSocket Server (Java Annotation을 사용한 서버코드)
클라이언트측에서 전달한 메시지를 다시 클라이언트에게 전송하는 Echo 기능의 서버
서버측에서는 클라이언트가 전달한 파라미터를 추출할 때는 Session 클래스를 사용하고, 파라미터가 아닌 데이터는 콜백 메소드의 아규먼트에 바로 전달된다
WEB-INF/classes/ 안의 패키지 경로에 위치
package my.websocket.test;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
@ServerEndpoint("/websocket/echoAnnotation")
public class MyWebSocket {
@OnOpen
public void echoOpen(Session session) {
}
//텍스트 데이터를 수신할 때 호출됨(session을 이용하여 파라미터를 추출할 수 있고 일반 데이터는 msg에 전달된다)
@OnMessage
public void echoTextMessage(Session session, String msg, boolean last) {
// 클라이언트가 요청할 때 전달한 파라미터(usr=홍길동)는 아래처럼 추출할 수 있다
String[] params = session.getQueryString().split("&");
String usr = params[0].split("=")[1];
try{
usr = URLDecoder.decode(usr,"EUC-KR");//파라미터로 전달된 데이터는 URLDecoder를 사용하여 복원한다
}catch(Exception e){
}
// JSON-Simple 라이브러리를 사용하여 JSON 문자열을 처리한다
JSONObject jobj = (JSONObject)JSONValue.parse(msg);
String usrName = (String)jobj.get("usrName");
String phone = (String)jobj.get("phone");
String content = (String)jobj.get("content");
try {
if (session.isOpen()) { //클라이언트 측으로 다시 전송한다
session.getBasicRemote().sendText(usrName+","+phone+","+content, last);
}
} catch (IOException e) {
try {
session.close();
} catch (IOException e1) {
// Ignore
}
}
}
// 바이너리 데이터를 수신할 때 호출됨
@OnMessage
public void echoBinaryMessage(Session session, ByteBuffer bb,
boolean last) {
try {
if (session.isOpen()) {
//session.getBasicRemote().sendBinary(bb, last);
session.getBasicRemote().sendText("파일이 서버에 도착했어요~", last);
File file = new File("D:/test/sample.txt");
// Set to true if the bytes should be appended to the file;
// set to false if the bytes should replace current bytes
// (if the file exists)
boolean append = false;
try {
// Create a writable file channel
FileChannel wChannel = new FileOutputStream(file, append).getChannel();
// Write the ByteBuffer contents; the bytes between the ByteBuffer's
// position and the limit is written to the file
wChannel.write(bb);
// Close the file
wChannel.close();
} catch (IOException e) {
}
}
} catch (IOException e) {
try {
session.close();
} catch (IOException e1) {
// Ignore
}
}
}
/**
* Process a received pong. This is a NO-OP.
*
* @param pm Ignored.
*/
@OnMessage
public void echoPongMessage(PongMessage pm) {
// NO-OP
}
@OnClose
public void onClose() {
// disconnection handling
}
@OnError
public void onError(Session session, Throwable throwable) {
// Error handling
}
}
클라이언트 측에서 서버로 전송하는 데이터는 문자열, 이미지, 파일 등이 해당된다
// Sending String
ws.send('your message');
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
ws.send(file);
클라이언트 측에서 수신하는 데이터의 타입 설정
클라이언트가 바이너리 데이터를 수신할 때 arraybuffer 타입인지 blob 타입인지를 설정할 수 있다.
디폴트 설정은 arraybuffer 타입이다
// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
ws.binaryType = 'arraybuffer';
ws.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};
클라이언트 측에서 이미지 파일을 수신하여 Canvas에 출력하는 예 ( HTML5 Javascript )
if(event.data instanceof ArrayBuffer)
{
var bytearray = new Uint8Array(event.data);
var tempcanvas = document.createElement('canvas');
tempcanvas.height = imageheight;
tempcanvas.width = imagewidth;
var tempcontext = tempcanvas.getContext('2d');
var imgdata = tempcontext.getImageData(0,0,imagewidth,imageheight);
var imgdatalen = imgdata.data.length;
for(var i=8;i<imgdatalen;i++)
{
imgdata.data[i] = bytearray[i];
}
tempcontext.putImageData(imgdata,0,0);
var img = document.createElement('img');
img.height = imageheight;
img.width = imagewidth;
img.src = tempcanvas.toDataURL();
chatdiv.appendChild(img);
chatdiv.innerHTML = chatdiv.innerHTML + "<br />";
}