익명 채팅방

package kh.spring.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}
package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class WebChat {

	private static Set<Session> clients = new HashSet<>();

	@OnOpen
	public void onConnect(Session client) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		for(Session client : clients) {

			if(!client.getId().contentEquals(session.getId())) {

				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(message);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}


	}

	@OnClose
	public void onClose(Session session) {
		clients.remove(session);
	}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://localhost/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>
	<!-- 웹소캣 라이브러리 추가 -->
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>
	

HTTP 프로토콜이 아니라 웹소캣으로 접속하기 때문에

ip 주소와 웹 정보를 가져올수없음

session configurator 

 

 

HTTPSessionConfigurator

object형을 리턴하기에 캐스팅 해줘야함

Map 방식

 

WebChat의 @ServerEndpoint 인자값 추가, 만든 httpSessionConfigurator 자료형 추가

핸드쉐이크로 만들어진 값이 config로 들어옴

 

다른 메서드에서도 쓸수 있도록

HomeController에서 session 사용 가능


서버에서 접속 url 수정 가능


package kh.spring.chat;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

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

import kh.spring.config.HTTPSessionConfigurator;

@ServerEndpoint(value="/chat",configurator = HTTPSessionConfigurator.class)
public class WebChat {

	private static Set<Session> clients = new HashSet<>();
	private HttpSession session;

	@OnOpen
	public void onConnect(Session client, EndpointConfig config) {
		System.out.println(client.getId()+" 클라이언트가 접속했습니다.");
		clients.add(client);
		this.session = (HttpSession) config.getUserProperties().get("session");
	}

	@OnMessage
	public void onMessage(Session session, String message) {
		
		String id = (String)this.session.getAttribute("loginId");
		
		for(Session client : clients) {
			if(!client.getId().contentEquals(session.getId())) {
				Basic basic = client.getBasicRemote();
				try {
					basic.sendText(id+ " : "+message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@OnClose
	public void onClose(Session session) {
		clients.remove(session);
	}
}
package kh.spring.config;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;

public class HTTPSessionConfigurator extends Configurator{

	@Override
    public void modifyHandshake(
    		ServerEndpointConfig sec, 
    		HandshakeRequest request, 
    		HandshakeResponse response) {
		
		HttpSession session = (HttpSession)request.getHttpSession();
		sec.getUserProperties().put("session", session);
        
    }

}
package kh.spring.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@Controller
public class HomeController {
	
	
	@Autowired
	private HttpSession session;
	

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {

		this.session.setAttribute("loginId", "Jack");
		
		return "home";
	}
	
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<style>
.chat-box {
	width: 500px;
	height: 500px;
	float: left;
}

.message-area {
	width: 100%;
	height: 90%;
	border: 1px solid #616161;
	overflow-y: auto;
	word-break: break-all;
}

.input-area {
	width: 100%;
	height: 10%;
	border: 1px solid #616161;
	overflow-y: auto;
}
</style>
<script>

	
	$(function() {
		var ws = new WebSocket("ws://192.168.60.7/chat")
		
		ws.onmessage = function(e){
			console.log(e.data);
			var line = $("<div>");
			line.append(e.data);
			$(".message-area").append(line);
		}

		
		
		$(".input-area").on("keydown", function(e) {
			if (e.keyCode == 13) {

				e.preventDefault();
				var text = $(".input-area").html();
				var line = $("<div>");
				line.append("글쓴이 : " + text);
				$(".message-area").append(line);
				$(".input-area").html("");
				
				ws.send(text);
				
				$('.message-area').scrollTop($('.message-area')[0].scrollHeight);
				return false;
			}
		})

	})
</script>
</head>
<body>
	<div class="chat-box">
		<div class="message-area"></div>
		<div class="input-area" contenteditable="true"></div>
	</div>
</body>
</html>

동시성 오류

 

동기화 작업 필요

 

 

HashSet 에 동기화 작업을 할 수 있도록 업그레이드

일반 HashSet은 동시성에 대한 취약성이 있다.

 

 


에러 처리

 

연결 끊어버리기

	@OnError
	public void onError(Session session, Throwable t) {
		clients.remove(session);
	}

 

메세지를 보내는 중 한명이 나갈경우

for문이 도는 동안 정지

없는 사람에게 보낼 수 없음

 

	@OnMessage
	public void onMessage(Session session, String message) {
		
		String id = (String)this.session.getAttribute("loginId");
		
		synchronized(clients){
			for(Session client : clients) {
				if(!client.getId().contentEquals(session.getId())) {
					Basic basic = client.getBasicRemote();
					try {
						basic.sendText(id+ " : "+message);
					} catch (IOException e) {}
				}
			}
		}

	}

굳이 나간 사람 때문에 발생한 에러 메시지를 출력할 필요 없음

+ Recent posts