ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹 소켓
    공부 2022. 8. 27. 12:24
    728x90

    웹소켓을 활용한 채팅

     

    jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    	pageEncoding="UTF-8"%>
    <%
    	String contextPath = request.getContextPath();
    int port = request.getLocalPort();
    String serverName = request.getServerName();
    %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Chat Client Socket</title>
    </head>
    <body>
    	<h1>웹소켓을 활용한 챗팅</h1>
    	<p>사용자 아이디를 입력 하고 로그인 하면 체팅이 시작 됩니다!</p>
    	<form>
    		사용자 : <input id="sender" type="text" value="user1"> 
    		<input id="loginBtn" value="Login" type="button"> 
    		<input id="closeBtn" value="Disconnect" type="button"><br> 
    		받는이: <select id="recipient">
    			<option value="All">All</option>
    		</select><br> 
    		메세지: <input id="txtMsg" type="text" value="hello"> 
    		<input id="sendBtn" value="Send" type="button">
    	</form>
    	<hr>
    	<textarea id="msgTxtArea" rows="10" cols="50"></textarea>
    	<script>
    // 웹소켓 프로토콜을 이용해서 서버와 연결
    // WebSocket은 HTML5에 기본 제공된다.
    
    var socket =null;
    var txt=document.getElementById("msgTxtArea");
    function connection(){
    	socket.onopen = function(message) {
    		console.log("onpen ...");
    		txt.value+=(">>>open...\n");
    	}
    	socket.onclose = function(message) {
    		txt.value+=(">>>onclose...\n");
    		console.log("onclose ...");
    	}
    	socket.onerror = function(err) {
    		txt.value+=(">>>error...\n");
    		console.log("소켓 오류", err);
    	}
    	socket.onmessage = function(message) {
    		try{
    			//var html="";
    			var html="<option value= All> All</option>";
    			var userList=JSON.parse(message.data);
    			console.log(userList);
    			for(var i in userList){
    				var user=userList[i];
    				html += "<option value=" + user  + ">"+ user + "</option>";
    			}
    			document.getElementById("recipient").innerHTML=html;
    		}catch(e){
    			txt.value+=(message.data+"\n");
    		}
    		
    		
    	}
    }
    loginBtn.onclick=function(){
    	var sender=document.getElementById("sender").value;
    	socket=new WebSocket("ws://<%=serverName%>:<%=port%>/<%=contextPath%>/broadSocket/"+sender);
    	connection();
    }
    sendBtn.onclick=function(){
    	var msg=document.getElementById("txtMsg").value;
    	var recip=document.getElementById("recipient").value;
    	txt.value+=("나: "+msg+"\n");
    	socket.send(recip+"|\|"+msg);
    }
    closeBtn.onclick=function(){
    	socket.close();
    }
    </script>
    
    </body>
    </html>

     

     

    소켓 java

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.security.cert.Extension;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.StringTokenizer;
    
    import javax.websocket.Extension.Parameter;
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.RemoteEndpoint.Basic;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    
    
    @ServerEndpoint(value="/broadSocket/{userID}")
    public class BroadSocket3 implements Runnable {
    	// 귓속말 메세지를 전달 할 session을 userID로 찾기 위해서 userID와 session을 맵핑한다.
    	static Map<String, Session> sessionMap = Collections.synchronizedMap(new HashMap<>());
    	static int mapSize;
    	static boolean sentFlag = false;
    	static String closeUserID = "";
    	public BroadSocket3() {
    		mapSize = sessionMap.size();
    		new Thread(this).start();;
    	}
    	
    	@OnOpen
    	public void onOpen(Session session, @PathParam("userID") String userID) throws IOException {
    		//System.out.println("소켓 서버 "+ session.getId() +"이(가) 오픈 되었습니다...");
    		
    		//System.out.println(Arrays.toString(session.getClass().getFields()) );
    		//System.out.println("session id : " + session.getId());
    		
    		// 쿼리 스트링을 받을 수 있다.
    		//System.out.println("session querystr : " + session.getQueryString());
    		// 문자열 리스트를 파라미터 맵으로 전달 받을 수 있다.
    		//Map<String, List<String>> params = session.getRequestParameterMap();
    		//System.out.println(params.get("sender"));
    		
    		// PathParam 어노테이션을 이용한 파라미터 전달
    		//System.out.println("path param sender : " + userID);
    		// 접속한 사용자의 session을 Map에 저장한다.
    		if(!sessionMap.containsKey(userID)&&!userID.equals("All")) {
    			sessionMap.put(userID, session);
    			synchronized(sessionMap) {
    				for(String userid : sessionMap.keySet()) {
    					Session sess = sessionMap.get(userid);
    					Set<String> keyset = sessionMap.keySet();
    					Iterator<String> iter = keyset.iterator();
    					StringBuffer strbf = new StringBuffer("[");
    					while(iter.hasNext()) {
    						strbf.append("\""+iter.next()+"\",");
    					}
    					strbf.deleteCharAt(strbf.lastIndexOf(","));
    					strbf.append("]");
    					sess.getBasicRemote().sendText(strbf.toString());
    				}
    				mapSize = sessionMap.size();
    			}
    			
    			
    			for(String userid : sessionMap.keySet()) {
    				(sessionMap.get(userid)).getBasicRemote().sendText(userID + "||님이 입장하였습니다!");
    			}
    		}else {
    			session.getBasicRemote().sendText("등록된 id입니다.");
    		}
    		//sessionMap.put(userID, session);	
    		
    	}
    	
    	@OnClose
    	public void onClose(Session session, @PathParam("userID") String userID) throws IOException {
    		System.out.println("소켓 서버"+ session.getId() +"이(가) 닫혔습니다...");
    		sessionMap.remove(userID);
    		closeUserID = userID;
    	}
    	
    	@OnMessage
    	public void onMessage(String message, Session session, @PathParam("userID") String userID) throws IOException {
    		System.out.println("받은 메세지 : " + message);
    		// clientSessionSet에서 클라이언트 세션을 가져와서 sender를 제외한 모든 세션에 메세지를 보낸다.
    		// 동기적으로 메세지를 보내야 한다.
    		synchronized(sessionMap) {			
    			//System.out.println("set size : " + sessionMap.size());
    			System.out.println(message);
    			StringTokenizer stk = new StringTokenizer(message, "||");
    			String recipient = stk.nextToken().trim();
    			String msg = stk.nextToken().trim();
    			if("All".equals(recipient)) {
    				for(String userid : sessionMap.keySet()) {
    					if(!userid.equals(userID)) {
    						//https://docs.oracle.com/javaee/7/api/javax/websocket/Session.html#getBasicRemote--
    						// 클라이언트 session에서 RemoteEndpoint 객체를 받아와서 메세지 텍스트를 보낸다.
    						// RemoteEndpoint를 통해서 동기적으로 메세지를 보낼 수 있다.
    						(sessionMap.get(userid)).getBasicRemote().sendText(userID + " : " + msg);
    					}
    				}
    			} else {
    				(sessionMap.get(recipient)).getBasicRemote().sendText(userID + " : " + msg);
    			}
    		}
    	}
    	
    	@OnError 
    	public void onError(Throwable th) {
    		th.printStackTrace();
    	}
    
    	@Override
    	public void run() {
    		while(true){
    			try {
    				Thread.sleep(1000);
    				if(mapSize != sessionMap.size()) {
    					synchronized(sessionMap) {
    						for(String userid : sessionMap.keySet()) {
    							Session session = sessionMap.get(userid);
    							Set<String> keyset = sessionMap.keySet();
    							Iterator<String> iter = keyset.iterator();
    							StringBuffer strbf = new StringBuffer("[");
    							while(iter.hasNext()) {
    								strbf.append("\""+iter.next()+"\",");
    							}
    							strbf.deleteCharAt(strbf.lastIndexOf(","));
    							strbf.append("]");
    							session.getBasicRemote().sendText(strbf.toString());
    							if(mapSize > sessionMap.size()) {
    								session.getBasicRemote().sendText(closeUserID+"님이 퇴장 하였습니다!");
    							}
    						}
    					}
    					mapSize = sessionMap.size();
    				}
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    728x90
    반응형

    '공부' 카테고리의 다른 글

    마크다운  (0) 2023.07.23
    HTML 이클립스 설정  (0) 2022.08.29
    H2 auto_increment 추가  (0) 2022.08.24
    서블릿  (0) 2022.08.16
    데이터베이스  (0) 2022.08.10

    댓글

Designed by Tistory.