웹소켓을 이용하여 사이트에 접속한 특정 이용자에게 파일을 전송하는 예
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="streamClient.jsp">채팅 페이지로 이동</a> </body> </html>
streamClient.jsp
<%@page import="java.util.List"%> <%@ 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> <title>Apache Tomcat WebSocket Examples: binary stream</title> <style type="text/css"> body { text-align: center;} div { display:inline-block; } input#chat { width: 410px } #console-container { 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; } canvas { border:1px solid black;} </style> <script type="text/javascript" src="jquery-2.1.4.min.js"></script> <script type="application/javascript"> var clientId = '<%=(String) session.getAttribute("id")%>'; var Chat = {}; var downloadFileName = ''; 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.\n'+Chat.socket.extensions); // 채팅입력창에 메시지를 입력하기 위해 키를 누르면 호출되는 콜백함수 document.getElementById('chat').onkeydown = function(event) { // 엔터키가 눌린 경우, 서버로 메시지를 전송함 if (event.keyCode == 13) { Chat.sendMessage(); } }; }; // 연결이 끊어진 경우에 호출되는 콜백함수 Chat.socket.onclose = function () { // 채팅 입력창 이벤트를 제거함 document.getElementById('chat').onkeydown = null; Console.log('Info: WebSocket closed.'); }; // 서버로부터 메시지를 받은 경우에 호출되는 콜백함수 Chat.socket.onmessage = function (evt) { console.log('수신 message.data:'+evt.data); if(evt.data instanceof Blob){ console.log('수신 데이터 타입:Blob'); saveData(evt.data, downloadFileName); downloadFileName = ''; }else if(evt.data instanceof ArrayBuffer){ console.log('수신 데이터 타입:ArrayBuffer'); saveData2(evt.data, downloadFileName); downloadFileName = ''; }else {// 파일이 아닌 텍스트인 경우 var jsonObj = eval('('+evt.data+')'); if('text' in jsonObj) { //채팅 메시지인 경우 Console.log(jsonObj.text); }else if('points' in jsonObj) { // 캔바스 그리기 좌표인 경우 alert('캔바스 그리기 좌표'); }else if('fname' in jsonObj) { // 파일이름이 수신된 경우 downloadFileName = jsonObj.fname; }else if('usrList' in jsonObj) {// 접속자 리스트가 수신된 경우 $('#receiver').empty(); var len = jsonObj.usrList.length; for(var i=0;i<len;i++){ if(i==0) { $('#receiver').append($('<option selected>').text(jsonObj.usrList[i])); }else { $('#receiver').append($('<option>').text(jsonObj.usrList[i])); } } } return; } }; }); // 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/stream'); } else { Chat.connect('wss://' + window.location.host + '/websocket/echoStreamAnnotation'); } }; // 서버로 메시지를 전송하고 입력창에서 메시지를 제거함 Chat.sendMessage = (function() { var message = document.getElementById('chat').value; if (message != '') { var receiver = $('select[name=receiver]').val(); var msg = {sender:clientId, receiver:receiver}; msg.text = message; Chat.socket.send(JSON.stringify(msg)); document.getElementById('chat').value = ''; } }); // 서버로 바이너리 데이터를 전송 Chat.sendBinary = (function() { // Blob, ArrayBuffer 둘 중 한가지 방법으로 전송 sendFileBlob(); // //sendFileArrayBuffer(); }); var Console = {}; // 화면에 메시지를 출력하기 위한 객체 생성 // log() 함수 정의 Console.log = (function(message) { var console = document.getElementById('console'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.innerHTML = message; console.appendChild(p); // 전달된 메시지를 하단에 추가함 // 추가된 메시지가 25개를 초과하면 가장 먼저 추가된 메시지를 한개 삭제함 while (console.childNodes.length > 25) { console.removeChild(console.firstChild); } // 스크롤을 최상단에 있도록 설정함 console.scrollTop = console.scrollHeight; }); // 위에 정의된 함수(접속시도)를 호출함 Chat.initialize(); function sendBinary() { Chat.sendBinary(); } // Blob 를 파일에 저장 function saveData(blob, fileName) { var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); }; // ArrayBuffer 를 파일에 저장 function saveData2(arrayBuffer, fileName) { var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; var parts = []; parts.push(arrayBuffer); url = window.URL.createObjectURL(new Blob(parts)); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); }; //canvas의 이미지 데이터를 서버로 전송하는 예 function sendImgArrayBuffer(){ // 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]; } Chat.socket.send(binary.buffer); }; //파일을 Blob를 서버로 전송함 function sendFileBlob() { // Sending file as Blob var file = document.querySelector('input[type="file"]').files[0]; var receiver = $('#receiver').val(); var msg = {sender:clientId, receiver:receiver}; msg.fname = file.name; //파일 데이터에 앞서 송,수신자, 파일명을 텍스트로 전송한다 Chat.socket.send(JSON.stringify(msg)); //파일 데이터를 전송한다 Chat.socket.send(file); } //파일을 ArrayBuffer를 서버로 전송함 function sendFileArrayBuffer() { var file = document.querySelector('input[type="file"]').files[0]; var fileReader = new FileReader(); fileReader.onload = function() { arrayBuffer = this.result; Chat.socket.send(arrayBuffer); }; fileReader.readAsArrayBuffer(file); } // 파일박스에서 선택된 파일의 이름을 구하는 방법 var filename = ''; function onChange(files){ filename = files[0].name; alert('선택변경:'+files[0].name); } </script> </head> <body ><p> 대화상대 선택<select id="receiver" name="receiver"> <% if(usrList!=null) { for(int i=0;i<usrList.size();i++) { %> <option><%=usrList.get(i)%></option> <% } } %> </select><br> <div> <p> <input type="text" placeholder="type and press enter to chat" id="chat" /> </p> <div id="console-container"> <div id="console"></div> </div> </div><br> <input type="file" onchange="onChange(this.files);"> <input type="button" value="바이너리 데이터 전송" onclick="sendBinary();"> <button type="button" id='btnClear' style="vertical-align: top;">캔바스 지우기</button> <br> <canvas width="600" height="480"></canvas> </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); } }
StreamAnnotation.java
package org.kdea.web.socket; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.util.*; import javax.servlet.http.HttpSession; import javax.websocket.EndpointConfig; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @ServerEndpoint(value="/websocket/stream", configurator=ServletAwareConfig.class) public class StreamAnnotation { Writer writer; OutputStream stream; private static Map<String, Session> sessionMap = new HashMap<>(); private HttpSession httpSession; @OnOpen public void start(Session session, EndpointConfig config) { System.out.println("클라이언트 접속됨 wsSession: "+session); System.out.println("웹소켓 서버측 config :"+config); //Session:접속자마다 한개의 세션이 생성되어 데이터 통신수단으로 사용됨 //한개의 브라우저에서 여러개의 탭을 사용해서 접속하면 Session은 서로 다르지만 HttpSession 은 동일함 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); //새로 접속한 이용자 리스트를 모든 이용자에게 전송한다 broadcast(userId, usrList); System.out.println("웹소켓 서버측 세션확인 httpSession :"+httpSession); } StringBuffer sb = new StringBuffer(); String sender, receiver, fname; @OnMessage public void textMessage(Session session, String msg, boolean last) throws IOException { sb.append(msg); if (last) { JSONParser jsonParser = new JSONParser(); try { JSONObject jsonObj = (JSONObject)jsonParser.parse(sb.toString()); receiver = (String)jsonObj.get("receiver"); sender = (String)jsonObj.get("sender"); fname = (String)jsonObj.get("fname"); if (writer == null) { writer = sessionMap.get(receiver).getBasicRemote().getSendWriter(); } System.out.println("수신자:"+receiver); } catch (ParseException e) { e.printStackTrace(); } System.out.println("완성된 텍스트:"+sb); writer.write(sb.toString()); writer.flush(); writer.close(); writer = null; sb.delete(0, sb.length()); } } ByteArrayOutputStream bout = new ByteArrayOutputStream(); @OnMessage public void binaryMessage(byte[] msg, Session session, boolean last) throws IOException { //System.out.println("클라이언트-->서버 바이너리 데이터 도착"); bout.write(msg); if (last) { if (stream == null) { stream = sessionMap.get(receiver).getBasicRemote().getSendStream(); } stream.write(bout.toByteArray()); stream.flush(); stream.close(); bout.reset(); stream = null; System.out.println("서버-->클라이언트 바이너리 전송완료"); fname = null; sender = null; receiver = null; } } private void broadcast(String sender, List<String> usrList) { Set<String> set = sessionMap.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String usrId = it.next(); if(usrId.equals(sender)) continue; JSONObject jsonObj = new JSONObject(); JSONArray jsonArr = new JSONArray(); jsonArr.addAll(0, usrList); jsonObj.put("usrList", jsonArr); try { Writer writer = sessionMap.get(usrId).getBasicRemote().getSendWriter(); writer.write(jsonObj.toJSONString()); writer.flush(); writer.close(); } catch (IOException e) { e.printStackTrace(); } } } @OnClose public void close() { String usrId = (String)httpSession.getAttribute("id"); sessionMap.remove(usrId); Object objList = httpSession.getServletContext().getAttribute("usrList"); List<String> usrList = (List<String>) objList; usrList.remove(usrId); } }