[ socket ~ chat 구성] : 실제론 다 요렇게 만들어야함
- HTML => 제작 (채팅 접속에 대한 로그인 정보)
- Socket Server => 구성
- Socket UDP,TCP => Room 정보
- Client (HTML, JSP) => Server에서 전달한 값을 출력
✅ 해당 예제에선 server 구성 / Client <-> Client 채팅까지만 제작해볼거임
( 그 다음 : Room 생성 및 DTO를 이용해서 생성하는 방식 제작 해야함 )
chrome 접속 -> simple websocket client 검색 -> Simple WebSocket Client - Chrome Web Store -> chrome에 추가
- websocketconfig.java : class
socket 서버 정보 및 접근 권한
package kr.co.sen.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.HandshakeInterceptor;
//socket 서버 정보 및 접근권한
@Configuration //환경설정 어노테이션
@EnableWebSocket //websocket 사용시 적용되는 어노테이션
public class websocketconfig implements WebSocketConfigurer{
//WebSocketConfigurer : socket에서 사용하는 interface - override 필쑤
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//방이 1개만 있을 경우
//registry.addHandler(sockethand(), "/chat").setAllowedOriginPatterns("*");
//각각의 room을 생성할 경우
registry.addHandler(sockethand(), "/chat/rooms/*")
.addInterceptors(handshake())
.setAllowedOriginPatterns("*");
}
//각 room마다 그룹을 생성하는 class를 로드하는 메소드
@Bean //가져온다
public HandshakeInterceptor handshake() {
return new chathandshake();
}
//새로운 클라이언트가 접속시 새로운 class를 생성하는 메소드
private WebSocketHandler sockethand(){
return new sockethand();
}
}
WebSocketConfigurer : socket에서 사용하는 interface - override 필쑤
- sockethand.java : class
socket으로 접속한 Client에 대한 메시지를 출력하는 class
package kr.co.sen.web;
import java.util.HashSet;
import java.util.Set;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class sockethand extends TextWebSocketHandler{
//TextWebSocketHandler : 문자로 메세지 전송하는 형태
//Set 배열 (Socket 전용 배열) - 접속한 클라이언트 정보가 담기는 배열
private final Set<WebSocketSession> sessions = new HashSet<>();
//add() : 접속한 client에 대한 정보값을 배열로 저장
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("새 클라이언트와 연결되었습니다.");
}
//getPayload() : client가 입력한 메세지를 getter로 로드함
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println(message.getPayload());
//반복문 사용 => for each로 모든 client에게 메세지를 발송
for(WebSocketSession con : sessions) {
con.sendMessage(message);
}
}
//client가 접속 종료시 해당 배열값의 내용을 삭제하면서 해제되도록 함.
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
System.out.println("클라이언트가 퇴장했습니다.");
}
}
- chathandshake.class
package kr.co.sen.web;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
//room을 생성하는 class
public class chathandshake implements HandshakeInterceptor{
//client가 room으로 입장하기 전 상황
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
String path = request.getURI().getPath();
System.out.println(path);
String roomid = path.substring(path.lastIndexOf("/")+1);
attributes.put("roomid", roomid);
return true;
}
//client가 room으로 입장 후 상황
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
//Database로 채팅 내용을 저장(Nosql ===> 채팅내용 저장시에는 무조건 Nosql)
}
}
✅ socket 실행 순서 - interface , 라이브러리 class
[websocketconfig.java]
1. WebSocketConfigurer (interface) : Socket 서버 환경설정
2. WebSocketConfigurer 요 파일 안의 WebsocketHandlers 메소드 실행 : Socket URL load
[sockethand.java]
3. Socket 형태 : Text(문자) or Binary(파일)
4. Memory에 Session 생성,삭제
[ URL 별도로 Session 운영시 - chathandshake.java ] ex) ws:localhost:801/chat/room/1
5. Handshakeinterceptor(interface)
❓ interceptor : 메모리와 서버간의 반복되는 코드에서 누락 관리 (많이 줄여줌)
6. session을 생성 [chat-room.java] - 여러개의 session을 생성 및 방번호를 기억
7. session 넘버링 [roomgen.java - static 이용하여 url 및 방 고유 id를 생성]
8. session 사용가능하게 websocketconfig.java 로 return [ roomrepo.java ]
- 방 리스트 및 생성된 고유 방 id를 가져오는 역할
👀 여러개의 session을 생성 및 방번호를 기억
- websocketconfig.java : class
package kr.co.sen.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.HandshakeInterceptor;
//socket 서버 정보 및 접근권한
@Configuration //환경설정 어노테이션
@EnableWebSocket //websocket 사용시 적용되는 어노테이션
public class websocketconfig implements WebSocketConfigurer{
//WebSocketConfigurer : socket에서 사용하는 interface - override 필쑤
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//방이 1개만 있을 경우
//registry.addHandler(sockethand(), "/chat").setAllowedOriginPatterns("*");
//각각의 room을 생성할 경우 ( ex) ws:localhost:8081/chat/rooms/1)
registry.addHandler(sockethand(), "/chat/rooms/{roomId}").setAllowedOriginPatterns("*");
}
//각 room마다 그룹을 생성하는 class를 로드하는 메소드
//@Bean //가져온다
public HandshakeInterceptor handshake() {
return new chathandshake();
}
//새로운 클라이언트가 접속시 새로운 class를 생성하는 메소드 (room이 여러개일 경우)
@Bean
public WebSocketHandler sockethand(){
return new sockethand();
}
}
- sockethand.java
package kr.co.sen.web;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class sockethand extends TextWebSocketHandler{
//TextWebSocketHandler : 문자로 메세지 전송하는 형태
//Set 배열 (Socket 전용 배열) - 접속한 클라이언트 정보가 담기는 배열(단일 session)
//private final Set<WebSocketSession> sessions = new HashSet<>();
@Autowired
private roomrepo roomrepo;
//ws로 접속시 해당 세션의 uri에 있는 마지막 번호를 원시배열로 생성하여 class배열로 전환
private Long getRoomid(WebSocketSession session) {
String uri = Objects.requireNonNull(session.getUri().toString());
String parts[] = uri.split("/");
String roomId = parts[parts.length-1];
return Long.parseLong(roomId);
}
//add() : 접속한 client에 대한 정보값을 배열로 저장
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//sessions.add(session);
Long roomId = getRoomid(session);
roomrepo.room(roomId).sessions().add(session);
System.out.println("새 클라이언트와 연결되었습니다.");
}
//getPayload() : client가 입력한 메세지를 getter로 로드함
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println(message.getPayload());
//반복문 사용 => for each로 모든 client에게 메세지를 발송
Long roomId = getRoomid(session);
chat_room room = roomrepo.room(roomId);
//for(WebSocketSession con : sessions) { //방 1개일때
for(WebSocketSession con : room.sessions()) {
//방이 여러개 있을 경우 해당 방에서만 채팅 내용 출력
con.sendMessage(message);
}
}
//client가 접속 종료시 해당 배열값의 내용을 삭제하면서 해제되도록 함.
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//sessions.remove(session);
Long roomId = getRoomid(session);
roomrepo.room(roomId).sessions().remove(session);
System.out.println("클라이언트가 퇴장했습니다.");
}
}
- chathandshake.class
얜 똑같음
package kr.co.sen.web;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
//room을 생성하는 class
public class chathandshake implements HandshakeInterceptor{
//client가 room으로 입장하기 전 상황
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
String path = request.getURI().getPath();
System.out.println(path);
String roomid = path.substring(path.lastIndexOf("/")+1);
attributes.put("roomid", roomid);
return true;
}
//client가 room으로 입장 후 상황
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
//Database로 채팅 내용을 저장(Nosql ===> 채팅내용 저장시에는 무조건 Nosql)
}
}
- chat_room.java - class
session으로 방을 생성하는 class
package kr.co.sen.web;
import java.util.HashSet;
import java.util.Set;
import org.springframework.web.socket.WebSocketSession;
//session으로 방을 생성하는 class
public class chat_room {
private Long id; //session id 예시)1234567784556445454
private String name; //방 주소 URL 이름
//복수 형태의 session을 생성하는 역할
private final Set<WebSocketSession> sessions = new HashSet<>();
//즉시실행 메소드 : id 생성 및 방 주소 url을 생성[roomgen.java와 함께 핸들링]
public static chat_room create(String name) {
chat_room room = new chat_room();
room.id = roomgen.createId(); //session id
room.name = name; //사용자가 접속한 url을 받음
return room;
}
//getter
public Long id() {
return id;
}
public Set<WebSocketSession> sessions(){
return sessions;
}
}
- roomgen.java - class
방 고유 id를 생성하는 class
package kr.co.sen.web;
//방 고유 id를 생성하는 class
public class roomgen {
private static Long id = 0L;
public static Long createId() {
id += 1;
return id;
}
}
- roomrepo.java
package kr.co.sen.web;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.stereotype.Repository;
@Repository
public class roomrepo {
private final Map<Long, chat_room> rooms;
public roomrepo() { //즉시실행
//방번호 리스트를 출력할 때 사용
rooms = Stream.of(chat_room.create("room1 채팅"),chat_room.create("room2 채팅"))
.collect(Collectors.toMap(chat_room::id, room->room));
}
// 고유 방 id getter 형태로 입력시키는 메소드
public chat_room room(Long id) {
return rooms.get(id);
}
}
repository를 썼다 => Autowired를 쓴다 (sockethand.java에서 씀)
====> 방 구분되어 채팅올라감
🔽 web으로 채팅 ?
web으로 GET 형태로 채팅을 참여하는 형태 구조의 Controller
@Controller
public class web_controller2 {
@Autowired
userDB_dao userDB_dao;
@GetMapping("/chat/rooms/{roomID}")
public ResponseEntity<List<websocketconfig>> roomin(@PathVariable Long roomid){
//사용정보 : 사용자 id, 사용자 이름, 사용자 전화번호
//gettersetter로 때리면 됨
userDB_dao.setUname("홍길동"); //머 요런식으로
return null;
}
}
websocket + javascript 연결방식
websocket.html
<body>
웹 소켓 주소 : <input type="text" id="socket_uri">
<input type="button" value="소켓 연결" onclick="connect_socket()">
</body>
<script>
function connect_socket(){
var uri = document.getElementById("socket_uri");
//input에 ws://localhost:8081/chat/rooms/2 입력
var socket = new WebSocket(uri.value);
socket.onopen = function(e){
socket.send("test");
console.log("서버 오픈!!")
}
}
</script>
요 밑에는 걍 참고용..^^.. 지우진 말쟈
👀 websocket
- websocketHandler.java : websocket을 open하는 class
package kr.co.sen.web;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Component
public class websocketHandler extends TextWebSocketHandler{
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload(); //해당 텍스트 내용을 인자값으로 받아서 문자화
//socket 서버 접속시 환영 메세지 출력
TextMessage textmessage = new TextMessage("Welcome Chatting Server~ ㅇㅅaㅇ");
session.sendMessage(textmessage);
}
}
- websocketConfig.java : websocket 환경설정하는 class
package kr.co.sen.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import lombok.RequiredArgsConstructor;
@Configuration //socket통신 환경설정
@EnableWebSocketMessageBroker //websocket 활성화 부분
public class websocketConfig implements WebSocketMessageBrokerConfigurer{
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub"); //클라이언트가 요청하는 경로
config.setApplicationDestinationPrefixes("/pub"); //메세지가 도착하는 경로
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-stomp").setAllowedOriginPatterns("*").withSockJS();
}
}
- chatroom.java : class
package kr.co.sen.web;
import java.util.UUID;
public class chatroom { //채팅방을 생성하는 DTO
private String roomid;
private String name;
//채팅방 이름을 랜덤하게 생성하는 메소드
public static chatroom create(String name) {
chatroom cr = new chatroom();
cr.roomid = UUID.randomUUID().toString();
return cr;
}
}
-chatcontroller.java : controller
package kr.co.sen.web;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Controller;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class chatcontroller { //채팅 소켓 통신 전용 컨트롤러
private final SimpMessageSendingOperations messaging;
//BroadCasting 기능을 수행하는 Mapping : socket 전용 Mapping (메세지 핸들링 전용)
@MessageMapping("chat/message")
public void message() {
messaging.convertAndSend("/sub/chat/room");
}
}
- chatroomrepo.java : class
/**/
하나두 모르겠따 ^^!
'CLASS > SPRINGBOOT' 카테고리의 다른 글
#4-3 / Oracle + Mybatis 회원가입 (0) | 2024.09.04 |
---|---|
#1-1 / 🌸 Gradle Project start + Oracle (0) | 2024.08.30 |
#10-3 / Ajax로 이미지 전송 및 JPA => 삭제 (0) | 2024.08.29 |
#10-2 / Ajax로 이미지 전송 및 JPA (0) | 2024.08.28 |
#10-1 / CDN 이미지 (0) | 2024.08.28 |