wsClient.jsp -> 뷰, 자바스크립트를 이용해서 서버와 통신한다


자바스크립트에 있는 WebSocket 객체를 이용해서 서버에 json 문자열을 전달한다




<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<script type="text/javascript" src="../js/jquery-2.2.2.min.js"></script>

<script type="text/javascript" src="../js/draw.js"></script>

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

<style type="text/css">

input#chat {

width: 410px

}


#console-container {

width: 400px;

float: left;

}


#console {

border: 1px solid #CCCCCC;

border-right-color: #999999;

border-bottom-color: #999999;

height: 177px;

overflow-y: scroll;

padding: 5px;

width: 100%;

}


#console p {

padding: 0;

margin: 0;

}


#guestList {

margin-right: 20px;

padding: 0px;

border: 1px solid #ccc;

width: 100px;

height: 210px;

float: left;

}


#guestList p {

margin: 0px;

padding: 0px;

}


canvas {

border: 1px solid #ccc;

}

</style>


<script type="application/javascript">

var Chat = {};

Chat.socket = null;

       // connect() 함수 정의

       Chat.connect = (function(host) {

           // 서버에 접속시도

           // 브라우져마다 WebSocket이 지원안 될수 있기 때문에 조건문으로 판단한다

           if ('WebSocket' in window) {// window 객체에 WebSocket 속성이 있다면 if문 실행

               Chat.socket = new WebSocket(host);// WebSocket은 브라우져가 제공하는 시스템 객체

           } else if ('MozWebSocket' in window) {

            // 아직 WebSocket이 표준이 안되서 브라우져마다 socket 객체를 만드는 것이 다르다

               Chat.socket = new MozWebSocket(host);

           } else {

               Console.log('Error: WebSocket is not supported by this browser.');

               return;

           }

            // 서버에 접속이 되면 호출되는 콜백함수

            // socket에 정의 된 속성에 함수주소를 저장

           Chat.socket.onopen = function () {

          // Console은 개발자가 만든 객체  

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

               // 채팅입력창에 메시지를 입력하기 위해 키를 누르면 호출되는 콜백함수

               $("#chat").on("keydown",function(event) {

                // 엔터키가 눌린 경우, 서버로 메시지를 전송함

                   if (event.keyCode == 13) {

                       Chat.sendMessage();

                   }

               });

              /*  document.getElementById('chat').onkeydown = function(event) {

                   // 엔터키가 눌린 경우, 서버로 메시지를 전송함

                   if (event.keyCode == 13) {

                       Chat.sendMessage();

                   }

               }; */

           };

           // 연결이 끊어진 경우에 호출되는 콜백함수

           Chat.socket.onclose = function () {

            // 채팅 입력창 이벤트를 제거함

            $("#chat").on("keydown",null);

               // document.getElementById('chat').onkeydown = null;

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

           };

           // 서버로부터 메시지를 받은 경우에 호출되는 콜백함수

           Chat.socket.onmessage = function (message) {

            // 수신된 메시지를 화면에 출력함

            var obj = eval(message.data);

            var ok = false;

            // $("#guestList").empty();

            for(var i = 0; i < obj.length; i++) {

            ok = false;

            if(obj[i].cmd == "start") {

            $("#guestList").find("p").each(function(idx) {

            if($(this).text() == obj[i].nickname) {

            ok = true;

           

            });

            if(!ok) {

            guestList.log("<input type=checkbox>"+obj[i].nickname);

            }

            } else if(obj[i].cmd == "msg") {

            Console.log(obj[i].nickname + " : " + obj[i].msg);

            } else if(obj[i].cmd == "end") {

            Console.log(obj[i].nickname + " : " + obj[i].msg);

           

            $("#guestList").find("p").each(function(idx) {

            if($(this).text() == obj[i].nickname) {

            $(this).remove();

            }

            });

            } else if(obj[i].cmd == "canvas") {

            if(obj[i].shape == "line") 

            draw.line(obj[i].endPosition, obj[i].startPosition);

            else 

            draw.shape(obj[i].endPosition, obj[i].startPosition, obj[i].shape);

            }

            }

           };

       });

    // connect() 함수 정의 끝

   

    // 위에서 정의한 connect() 함수를 호출하여 접속을 시도함

       Chat.initialize = function() {

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

               //Chat.connect('ws://' + window.location.host + '/websocket/chat');

               // connect 함수에 파라미터를 전달해서 접속 시도, 콜백함수 등록

               // ws : tcp/ip 기반 프로토콜

            Chat.connect('ws://192.168.8.19:7777/JavaWEB/websocket/chat');

           } else {

               Chat.connect('wss://' + window.location.host + '/websocket/chat');

           }

       };

       

    var draw = {};

   

    draw.shape = (function(ep,sp,shape) {

    ctx.beginPath();

    if(shape == "square") {

    ctx.fillRect(sp.x, sp.y, ep.x - sp.x, ep.y - sp.y);

    } else if(shape == "circle") {

    ctx.arc(sp.x, sp.y, ep.x - sp.x, Math.PI * 2, false);

    }

    ctx.fillStyle = colorCode;

      ctx.fill();

    ctx.closePath();

    });

   

    draw.line = (function(ep,sp) {

    ctx.beginPath();

ctx.moveTo(sp.x, sp.y);

ctx.lineTo(ep.x, ep.y);

ctx.lineWidth = 10;

ctx.strokeStyle = colorCode;

ctx.stroke();

ctx.closePath();

    });

   

       // 서버로 메시지를 전송하고 입력창에서 메시지를 제거함

       Chat.sendMessage = (function() {

        var message = $("#chat").val();

        var arr = [];

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

           if (message != '') {

            var obj = {};

            var jsonStr;

            $("#guestList p").find("input[type=checkbox]").each(function(idx) {

            if($(this).is(":checked")) {

            console.log($(this).parent().text());

            arr.push($(this).parent().text());

            obj.cmd = "msg";

                obj.msg = message;

                obj.nickArr = arr;

                jsonStr = JSON.stringify(obj);

            }

            });

            Chat.socket.send(jsonStr);

           

               $("#chat").val("");

               // document.getElementById('chat').value = '';

           }

       });

       

       Chat.sendCanvas = (function() {

        var obj = {};

        var jsonStr;

        var arr = [];

        $("#guestList p").find("input[type=checkbox]").each(function(idx) {

            if($(this).is(":checked")) {

            arr.push($(this).parent().text());

            obj.cmd = "canvas";

            if(command == "line") {

            obj.endPosition = p2;

                    obj.startPosition = p1;

            } else {

                    obj.endPosition = ep;

                    obj.startPosition = sp;

            }

                obj.shape = command;

                obj.nickArr = arr;

                jsonStr = JSON.stringify(obj);

            }

            });

       

        Chat.socket.send(jsonStr);

       });

       

       var guestList = {};

        // 접속한 사용자리스트를 추가하는 메소드

       guestList.log = (function(id) {

        var $listBox = $("#guestList");

        $listBox.append("<p></p>");

        $listBox.find("p:last").html(id);

        $listBox.scrollTop($listBox.prop("scrollHeight"));

       });

       var Console = {}; // 화면에 메시지를 출력하기 위한 객체 생성, json Object

       // log() 함수 정의

       Console.log = (function(message) {

        var $console = $("#console");

        $console.append("<p/>");

        $console.find("p:last").css("wordWrap","break-word");

        $console.find("p:last").html(message);

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

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

           // wordWrap 문자열이 창 최대 오른쪽 벽에 닿으면 줄바꿈

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

           p.innerHTML = message;

           console.appendChild(p); // 전달된 메시지를 하단에 추가함

           // 추가된 메시지가 25개를 초과하면 가장 먼저 추가된 메시지를 한개 삭제함 */

           while ($console.find("p").length > 25) {

            $console.find("p:first").remove();

           }

           $console.scrollTop($console.prop("scrollHeight"));

           

           /* while (console.childNodes.length > 25) {

               console.removeChild(console.firstChild);

           } */

           // 스크롤을 최상단에 있도록 설정함

           

           // console.scrollTop = console.scrollHeight;

       });

       // 위에 정의된 함수(접속시도)를 호출함

       Chat.initialize();

       // $("<p/>")// $("<p></p>") // jQuery 함수를 이용하여 메모리에 p 태그 DOM 객체를 생성한다



</script>

</head>

<body>

<div id="guestList">

<input type="checkbox" id="all"><span>전체</span>

</div>

<div>

<p style="margin: 0px;">

<input type="text" placeholder="type and press enter to chat" id="chat" />

</p>

<div id="console-container">

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

</div>

<div style="clear: both;"></div>

</div>

<canvas id="chatCanvas" width="800" height="600"></canvas>

<button type="button" id="line" onclick="selector('line');">선</button>

<button type="button" id="square" onclick="selector('square');">사각형</button>

<button type="button" id="circle" onclick="selector('circle');">원</button>

<input type="color" id="select-color">

</body>

</html>


WebSocketServer.java -> 서버, Session을 통해서 사용자로부터 데이터를 전달 받는다.


package jun.mvc.websocket;


@ServerEndpoint(value = "/websocket/chat") // 클라이언트가 접속할 때 사용될 URI

public class WebSocketServer {

private static JSONArray jsonArr = new JSONArray();

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

private static final String GUEST_PREFIX = "Guest";

// AtomicInteger 클래스는 getAndIncrement()를 호출할 때마다 카운터를 1씩 증가하는 기능을 가지고 있다

private static final AtomicInteger connectionIds = new AtomicInteger(0);

// CopyOnWriteArraySet 을 사용하면 컬렉션에 저장된 객체를 좀더 간편하게 추출할 수 있다

// 예를 들어, toArray()메소드를 통해 쉽게 Object[] 형의 데이터를 추출할 수 있다.

private static final Set<WebSocketServer> connections = new CopyOnWriteArraySet<WebSocketServer>();


private final String nickname;

// 클라이언트가 새로 접속할 때마다 한개의 Session 객체가 생성된다.

// Session 객체를 컬렉션에 보관하여 두고 해당 클라이언트에게 데이터를 전송할 때마다 사용한다

private Session session;

private JSONParser jsonParser = new JSONParser();


public WebSocketServer() { // 클라이언트가 새로 접속할 때마다 이 클래스의 인스턴스가 새로 생성됨

// 클라이언트가 접속할 때마다 서버측에서는 Thread 가 새로 생성되는 것을 확인할 수 있다

String threadName = "Thread-Name:" + Thread.currentThread().getName();

// getAndIncrement()은 카운트를 1 증가하고 증가된 숫자를 리턴한다

nickname = GUEST_PREFIX + connectionIds.getAndIncrement();

System.out.println(threadName + ", " + nickname);

}


// 메소드명은 개발자가 지정할 수 있다.

// 애노테이션에 따라 구분 된다.

@SuppressWarnings("unchecked")

@OnOpen

public void start(Session session) {// 통신은 스트림으로 한다, 즉 세션에서 스트림을 얻을 수 있다.

System.out.println("클라이언트 접속됨 " + session);

// Session:접속자마다 한개의 세션이 생성되어 데이터 통신수단으로 사용됨, 즉 접속자 마다 구분됨

// 한개의 브라우저에서 여러개의 탭을 사용해서 접속하면 Session은 서로 다르지만 HttpSession 은 동일함

this.session = session;

connections.add(this);// 사용자 세션이 저장된 객체를 connections Set객체에 저장

sessionMap.put(nickname, session);// 사용자 nickname을 키값으로 하고 session을 value로 하는 맵객체에 추가

JSONObject jsonObj = new JSONObject();

jsonObj.put("cmd", "start");// 클라이언트쪽에서 해당하는 작업을 하기 위해 명령어를 String으로 준다

jsonObj.put("nickname", nickname);

jsonObj.put("msg", "has joined");


jsonArr.add(jsonObj);

String jsonStr = jsonArr.toJSONString();

this.sendGuestList(jsonStr);

}


@SuppressWarnings("unchecked")

@OnClose

public void end() {

connections.remove(this);// 사용자가 접속을 중단하면 set에서 해당 사용자 객체를 삭제

sessionMap.remove(nickname);

JSONObject jsonObj = new JSONObject();

jsonObj.put("cmd", "end");

jsonObj.put("nickname", nickname);

jsonObj.put("msg", "has disconnected.");

JSONArray endArr = new JSONArray();

endArr.add(jsonObj);

try {

for(int i = 0; i < jsonArr.size(); i++) {

JSONObject obj = (JSONObject) jsonParser.parse(jsonArr.get(i).toString());

if(obj.get("nickname").equals(nickname)) {

jsonArr.remove(i);

}

}

} catch (Exception e) {

e.printStackTrace();

}

String jsonStr = endArr.toJSONString();

this.sendGuestList(jsonStr);

}


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

@OnMessage

public void incoming(String json) {

String threadName = "Thread-Name:" + Thread.currentThread().getName();

System.out.println(threadName + ", " + nickname);

if (json == null || json.trim().equals(""))

return;

JSONObject jsonObj;

JSONArray arr;

try {

jsonObj = (JSONObject) jsonParser.parse(json);// 클라이언트쪽에서 문자열로 넘어온 json오브젝트를 jsonObject로 만들어준다

System.out.println(jsonObj);

arr = (JSONArray) jsonObj.get("nickArr");

this.selectNickToMsg(arr, jsonObj);

} catch (ParseException e) {

e.printStackTrace();

}

}


@SuppressWarnings("unchecked")

private void selectNickToMsg(JSONArray arr, JSONObject jsonObj) {

JSONArray jsonArr = new JSONArray();


jsonObj.put("nickname", nickname);

jsonArr.add(jsonObj);

try {

for (int i = 0; i < arr.size(); i++) {

String sNick = (String) arr.get(i);

if(!sNick.equals(nickname))

sessionMap.get(sNick).getBasicRemote().sendText(jsonArr.toJSONString());

}

session.getBasicRemote().sendText(jsonArr.toJSONString());

} catch (IOException e) {

e.printStackTrace();

}

}


@OnError

public void onError(Throwable t) throws Throwable {

System.err.println("Chat Error: " + t.toString());

}

// 현재 세션으로부터 도착한 메시지를 모든 접속자에게 전송한다

private void sendGuestList(String jsonStr) {

Iterator<WebSocketServer> ss = connections.iterator();

for (int i = 0; i < connections.size(); i++) {

WebSocketServer client = ss.next();

try {

synchronized (client) {// 여러 사용자의 쓰레드에서 이 메소드에 접근하기 때문에 rock을 건다

// 서버에 접속 중인 모든 이용자에게 메지지를 전송한다

// getBasicRemote()로 사용자의 스트림을 얻는다


client.session.getBasicRemote().sendText(jsonStr);

}

} catch (IllegalStateException ise) {

// 특정 클라이언트에게 현재 메시지 보내기 작업 중인 경우에 동시에 쓰기작업을 요청하면 오류 발생함

if (ise.getMessage().indexOf("[TEXT_FULL_WRITING]") != -1) {

new Thread() {

@Override

public void run() {

// 같은 에러가 발생하면 반복문을 통해서 다시 메세지를 전달하게 한다.

while (true) {

try {

client.session.getBasicRemote().sendText(jsonStr);

break;

} catch (IllegalStateException _ise) {

try {

Thread.sleep(100); // 메시지 보내기 작업을 마치도록

// 기다려준다

} catch (InterruptedException e) {

}

} catch (IOException ioe) {

ioe.printStackTrace();

}

}

}

}.start();

}

} catch (Exception e) {

// 메시지 전송 중에 오류가 발생(클라이언트 퇴장을 의미함)하면 해당 클라이언트를 서버에서 제거한다

System.err.println("Chat Error: Failed to send message to client:" + e);

connections.remove(client);

try {

// 접속 종료

client.session.close();

} catch (IOException e1) {

// Ignore

}

// 한 클라이언트의 퇴장을 모든 이용자에게 알린다

String message = String.format("* %s %s", client.nickname, "has been disconnected.");

this.sendGuestList(message);

}

}

}

}

'JAVASCRIPT > Basic' 카테고리의 다른 글

Json Array 사용 예제  (0) 2016.03.21