본문 바로가기

WebSocket/Canvas Drawing

Canvas Drawing with WebSocket

웹소켓을 이용한 원격 캔바스에 그림 그리기 예제


개요

로그인한 특정 이용자의 리스트 중에서 선택된 한 이용자의 캔바스에 마우스 드래그로 그림을 그려 보일 수 있는 웹소켓 예제이다


테스트 환경

Windows 7, JDK 8, JSP, Tomcat 8, HTML 5, jQuery 2


loginForm.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>로그인</title>
</head>
<body>
<p>
<form action="loginProc.jsp" method="post">
I D <input type="text" name="id" value="user01">
PWD <input type="password" name="pwd" value="1111">
<button type="submit">로그인</button>
</form>
</body>
</html>



loginProc.jsp

<%@page import="java.util.*"%>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
	request.setCharacterEncoding("utf-8");
	String id = request.getParameter("id");
	String pwd = request.getParameter("pwd");
	if(id!=null && !id.equals("")) {
		session.setAttribute("id", id);
	}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> 로그인 결과 </title>
</head>
<body>
<p>
<a href="canvas_network.jsp">네트웍 그리기</a>
</body>
</html>



canvas_network.jsp

<%@page import="java.util.*"%>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
	Object objList = application.getAttribute("usrList");
	List<String> usrList = null;
	if(objList!=null) usrList = (List<String>) objList;
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>네트워크 캔바스 예제</title>
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
<style type="text/css">
	body { margin:0px 0px; text-align: center; }
	canvas { display:inline-block; border: 1px solid black; }
</style>
<script type="text/javascript">
var clientId = '<%=(String) session.getAttribute("id")%>';
var Chat = {};

Chat.socket = null;

// connect() 함수 정의
Chat.connect = (function(host) {
    // 서버에 접속시도
    if ('WebSocket' in window) {
        Chat.socket = new WebSocket(host);
    } else if ('MozWebSocket' in window) {
        Chat.socket = new MozWebSocket(host);
    } else {
        Console.log('Error: WebSocket is not supported by this browser.');
        return;
    }

     // 서버에 접속이 되면 호출되는 콜백함수
    Chat.socket.onopen = function () {
        console.log('Info: WebSocket connection opened.');
    };
	
    // 연결이 끊어진 경우에 호출되는 콜백함수
    Chat.socket.onclose = function () {
        console.log('Info: WebSocket closed.');
    };
	
    // 서버로부터 메시지를 받은 경우에 호출되는 콜백함수
    Chat.socket.onmessage = function (message) {
    	var jsonObj = eval('('+message.data+')');
    	if('clear' in jsonObj) {
    		clearCanvas();
    	}
    	else if('content' in jsonObj) {
    		var jsonLine = jsonObj.content;
			drawLine(jsonLine);
    	}
    };
});
	// connect() 함수 정의 끝
	
	// 위에서 정의한 connect() 함수를 호출하여 접속을 시도함
Chat.initialize = function() {
    if (window.location.protocol == 'http:') {
        //Chat.connect('ws://' + window.location.host + '/websocket/chat');
    	Chat.connect('ws://192.168.8.32:8888/MyWeb/websocket/chat');
    } else {
        Chat.connect('wss://' + window.location.host + '/websocket/chat');
    }
};

// 서버로 메시지를 전송하고 입력창에서 메시지를 제거함
Chat.sendMessage = (function(jsonObj) {
    if (jsonObj != null) {
        Chat.socket.send(JSON.stringify(jsonObj));
    }
});

</script>

<script type="text/javascript">
var cnt = 0;
var ctx = null;
var x1=y1=x2=y2=0;
var isDrag = false;
var ptArr = new Array();
var timer = null;
var jsonStr = '';

$(function(){
	var $canvas = $('canvas').eq(0);
	ctx = $canvas[0].getContext("2d");
	
	$canvas.on('mousedown', function(evt){
		x1 = evt.pageX - this.offsetLeft;
		y1 = evt.pageY - this.offsetTop;

		isDrag = true;
	});
	
	$canvas.on('mouseup', function(evt){
		isDrag = false;
	});

	$canvas.on('mousemove', function(evt){
		if(isDrag) {
			x2 = evt.pageX-this.offsetLeft;
			y2 = evt.pageY-this.offsetTop;
			
			var receiver = $('select[name=receiver]').val();
		    var msg = {sender:clientId, receiver:receiver};
		    msg.content = {x1:x1, y1:y1, x2:x2, y2:y2};

			Chat.sendMessage(msg);
			
			x1 = x2;
			y1 = y2;
		}
	});
	
	$('#btnClear').eq(0).on('click',function(){
		var receiver = $('select[name=receiver]').val();
		Chat.sendMessage({sender:clientId, receiver:receiver, clear:true});
	});
	
	Chat.initialize();
});

function drawLine(jsonLine) {

	ctx.strokeStyle = "#ff5533";
	ctx.lineJoin = "round";
	ctx.lineWidth = 5;

	ctx.beginPath();

	ctx.moveTo(jsonLine.x1, jsonLine.y1);

	ctx.lineTo(jsonLine.x2, jsonLine.y2);
	
	ctx.closePath();
	ctx.stroke();
}

function clearCanvas() {
	ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}

</script>
</head>
<body>
	대화상대 선택<select id="receiver" name="receiver">
	<% 
		if(usrList!=null) {
			for(int i=0;i<usrList.size();i++) { %>
				<option><%=usrList.get(i)%></option>
	<% 		} 
		}
	%>
	</select><br>
<canvas width="600" height="480"></canvas>
<button type="button" id='btnClear' style="vertical-align: top;">캔바스 지우기</button>
</body>
</html>




ServletAwareConfig.java

package org.kdea.web.socket;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
      
public class ServletAwareConfig extends ServerEndpointConfig.Configurator {
 
    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
         
        HttpSession session = (HttpSession) request.getHttpSession();
        //ServletContext ctx = session.getServletContext();
 
        config.getUserProperties().put(HttpSession.class.getName(), session);
        //config.getUserProperties().put(ServletContext.class.getName(), ctx);
    }
}



ChatAnnotation.java

package org.kdea.web.socket;

import java.io.IOException;
import java.util.*;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.json.simple.*;
import org.json.simple.parser.*;

@ServerEndpoint(value = "/websocket/chat", configurator=ServletAwareConfig.class) //클라이언트가 접속할 때 사용될 URI
public class ChatAnnotation {

    private static Map<String, Session> sessionMap = new HashMap<>();

	private HttpSession httpSession;

    public ChatAnnotation() {
    }


    @OnOpen
    public void start(Session session, EndpointConfig config) {
  
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String userId = (String)httpSession.getAttribute("id");
        System.out.println("접속한 클라이언트 ID:"+userId);
        
        sessionMap.put(userId, session);
        Object objList = httpSession.getServletContext().getAttribute("usrList");
		if(objList==null) {
			List<String> usrList = new ArrayList<>();
			httpSession.getServletContext().setAttribute("usrList", usrList);
			objList = usrList;
		}
		List<String> usrList = (List<String>) objList;
		usrList.add(userId);
    }


    @OnClose
    public void end() {
    	String usrId = (String)httpSession.getAttribute("id");
    	sessionMap.remove(usrId);
    	
    	Object objList = httpSession.getServletContext().getAttribute("usrList");
    	List<String> usrList = (List<String>) objList;
    	usrList.remove(usrId);
    }

    // 현재 세션과 연결된 클라이언트로부터 메시지가 도착할 때마다 새로운 쓰레드가 실행되어 incoming()을 호출함
    @OnMessage
    public void incoming(String message) {

        if(message==null || message.trim().equals("")) return;

        JSONParser jsonParser = new JSONParser();
        try {
			JSONObject jsonObj = (JSONObject)jsonParser.parse(message);
			String sender = (String)jsonObj.get("sender");
			String receiver = (String)jsonObj.get("receiver");
			
			try {
				sessionMap.get(receiver).getBasicRemote().sendText(message);
				sessionMap.get(sender).getBasicRemote().sendText(message);
				return;
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
    }

    
    @OnError
    public void onError(Throwable t) throws Throwable {
        System.err.println("Chat Error: " + t.toString());
    }
}