12/07 WebSocket

yookyungmin·2022년 12월 7일
0

WebSocket

  • 채팅

  • 비트코인 거래소

  • 리퀘스트 없이 리스폰스 할수 없다

  • Request를 주기적으로 보내서 Response하는데

  • Websocet을 쓰면 누군가는 보내고 누군가는 받는게 매끄럽게 이어진다

  • HTTP 프로토콜 특성상 선 Request > 후 Response 형태를 통신 규칙을 지켜야 하기 때문에 구현하기 어려운 일부 컨텐츠가 비효율적으로 제작됨 이러한 HTTP 특성을 넘어서 일반 네트워크 소켓 통신으로 데이터를 주고 받는기술 Websoclk

웹소캣 maven

<!-- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api -->
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

> 프로토콜들은 핸드쉐이킹 과정을 통해 주고받는다

=======================

package kh.spring.endpoint;

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

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 com.google.gson.JsonObject;

import kh.spring.configurator.WSConfigurator;



@ServerEndpoint(value="/chat", configurator = WSConfigurator.class)
public class ChatEndpoint {


	//webSocket 이 처음 연결되었을떄 실행될 함수

	//접속한 사용자 Session 을 모아두는 컬렉션
	private  static Set<Session> clients = Collections.synchronizedSet(new HashSet<>());
	//static이 필요한 이유, 사용자마다 ChatEndpin는 하나씩 생성 되는데 static을붙이면 한개 list에 사용자 정보들이 담긴다 /static은 한번 생성 되기 떄문에 객체 생성없이 사요악능
	//접속자들의 정보를 저장하기 위해 Session 지역변수로 사용하면 안되고 멤버필드로 사용해야 함 컬렉션 set 필요 중복허용 x

	private HttpSession hSession;
	//접속자의HttpSession객체를 저장할메서드
	//접속하는 사람마다 세션을 따로따로 만들어줘야되기떄문에 Autowired사용x
	
	@OnOpen
	public void onConnection(Session client, EndpointConfig config) { //websocket session

		System.out.println("웹 소캣 연결 확인");
		clients.add(client);
		
		this.hSession = (HttpSession)config.getUserProperties().get("hSession");
		
		System.out.println(this.hSession.getAttribute("loginID")+"가 채팅방에 입장하였습니다");//핸드쉐이킹이 끝나고넘어옴
	}//웹소캣 연결


	@OnMessage
	public void onMessage(String msg) { //throws Exception하면 에러가 생길시 다른 사람들이 메시지 확인을못한다 그래서 try catch

		System.out.println("도착한 메시지"+ msg);
		
		msg = msg.replace("<", "&lt;");
		
		JsonObject data = new JsonObject();
		data.addProperty("ip", (String)this.hSession.getAttribute("IP"));
		data.addProperty("sender", (String)this.hSession.getAttribute("loginID"));
		data.addProperty("msg", msg);
		
		synchronized (clients) {
				//for문이 동작중 사용자가 종료했을때 에러나는 상황, 동시성 오류 방지

			for(Session client : clients) {//for문 돌다가 사용자가 종료해서 @Onclose에 의해 삭제되면 동시성 오류가  생긴다 ex)10개로 for문돌다가 9개로 바뀌면 for문 돌아가는 도중 갯수는 변경되면 안된다
				try {
					client.getBasicRemote().sendText(data.toString());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	@OnClose //연결 종료 정보제거
	public void onClose(Session client) {
		clients.remove(client);
	}
	@OnError
	public void onError(Session client, Throwable t) { //에러가 난 사용자 제거
		clients.remove(client);
	}
}
package kh.spring.configurator;

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 WSConfigurator extends Configurator{

	
@Override    //헨드쉐이킹 과정을 
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
	HttpSession session = (HttpSession)request.getHttpSession();
	sec.getUserProperties().put("hSession", session);
	//
}
	 
}
package kh.spring.controller;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
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(HttpServletRequest request, String id) {
		session.setAttribute("IP", request.getRemoteAddr());
		session.setAttribute("loginID", id);
		return "home";
	}
	
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>


<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.6.1.js"></script>
<style>
/* div{
                            border: 1px solid black;
                        } */
.container {
	width: 500px;
	height: 100%;
	margin: auto;
}

.box1 {
	height: 400px;
	border: 1px solid black;
	overflow-y: auto;
	background-color: wheat;
}

.box2 {
	height: 150px;
	border: 1px dotted black;
}

.box2>#input {
	width: 100%;
	height: 80%;
	background-color: lavender;
	overflow-y: auto;
	text-align: top;
}

.box2>#emo {
	width: 30px;
	height: 30px;
	background-color: blueviolet;
}

#emo>button {
	width: 30px;
	height: 30px;
}

button>img {
	width: 100%;
	height: 100%;
}

.textbubble {
	position: relative;
	display: inline-block;
	/* max-width: calc(100% - 70px); */
	padding: 10px;
	margin-top: 7px;
	font-size: 15px;
	border-radius: 10px;
	left: 10px;
	background-color: white;
}

#emobox {
	width: 100%;
	height: 100%;
	background-color: beige;
	display: none;
}

.emoti {
	width: 80px;
	height: 80px;
}

.msg-box {
	max-width: 250px;
	word-wrap: break-word;
	border: 1px dotted black;
	border-radius: 5px;
	margin: 5px;
	padding: 5px;
	display: inline-block;
}
</style>
<script>
	function updateScroll() {
		var element = document.getElementsByClassName("box1")[0];
		element.scrollTop = element.scrollHeight;
	}

	$(function() {
		let ws = new WebSocket("ws://192.168.150.35/chat");//webSocket 인스턴스생성
		//연결	
		//@OnOpen

		ws.onmessage = function(e) {
			console.log(e.data);
			let data = JSON.parse(e.data);
			let text = e.data;
			let outer = $("<div>");
			let line = $("<div>");

			line.addClass("msg-box")
			line.append(data.msg);

			outer.append(line);
			$(".box1").append(outer);
			updateScroll();
		}

		$("#input").on("keydown", function(e) {
			if (e.keyCode == 13) {
				let text = $("#input").text();
				$("#input").text("");
				ws.send(text); //@OnMessage로
				return false;
			}
		});
	})
</script>
</head>

<body>

	<div class="container">
		<div class="box1" id="chat"></div>
		<div class="box2">
			<div id="input" contenteditable="true"></div>
			<button>전송</button>
			<div id="emo">
				<button id="emoadd">
					<img src="emoticon1.png" id="econ01">
				</button>
			</div>


		</div>
		<div id="emobox">

			<img src="emoticon1.png" id="econ01" class="emoti"> <img
				src="emoticon2.gif" id="econ02" class="emoti"> <img
				src="emoticon3.gif" id="econ02" class="emoti"> <img
				src="emoticon4.gif" id="econ02" class="emoti"> <img
				src="emoticon5.gif" id="econ02" class="emoti"> <img
				src="emoticon6.gif" id="econ02" class="emoti">

		</div>

	</div>

</body>


</html>

guava 메시지 최근 30개 저장을 위한 라이브러리

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version>
</dependency>

0개의 댓글