웹소켓 구현

728x90

 

스프링부트 서버부분(CircleWebSocketHandler.java)

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.temswin.circle.dto.SocketBuyData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class CircleWebSocketHandler extends TextWebSocketHandler {
	
    // 세션 저장소 -> 겹치지않도록 SET으로 저장한다.
    private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
    
    // 웹 소켓 연결
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String clientId = getClientIdFromSession(session);

        System.out.println("✅ 클라이언트 연결됨: " + clientId);
        session.sendMessage(new TextMessage(buildJsonMessage("connect", "웹소켓 연결 성공")));
    }
    
    
    //수신된 메시지    
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message){
        try {
            String clientId = getClientIdFromSession(session);
            
            String response = message.getPayload();
            
        }catch (Exception e){
            e.printStackTrace();
        }
    }

	// 웹소켓 연결 종료
    @Override
    public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status){
        try {
            sessions.remove(session);
        }catch (Exception ignored){}
        
        System.out.println(buildJsonMessage("close","클라이언트 연결 종료"));
    }

    public boolean sendMessageToClient(String clientId, String type,String message) {
        WebSocketSession session = null;
        for (WebSocketSession s : sessions) {
            String idFromSession = getClientIdFromSession(s);
            if (clientId.equals(idFromSession)) {
                session = s;
                break;
            }
        }

        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(buildJsonMessage(type, message)));
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    //json 형식 변환
    private String buildJsonMessage(String type, String message) {
        Map<String, Object> map = new HashMap<>();
        map.put("type", type);
        map.put("message", message);
        map.put("timestamp", new Date());

        try {
            return objectMapper.writeValueAsString(map);
        } catch (Exception e) {
            return "{\"type\":\"error\",\"message\":\"메시지 생성 실패\"}";
        }
    }
    
    // 클라이언트 ID 쿼리 파라미터 추출 예시
    private String getClientIdFromSession(WebSocketSession session) {
        URI uri = session.getUri();
        if (uri == null) return null;

        Map<String, String> params = getQueryParams(uri);
        return params.get("clientId");
    }
    
}

 

 

자바스크립트 클라이언트 부분(sockets.js)

let viewClientId = "view_" + Math.random().toString(36).substring(2, 10);

const socket = new WebSocket(`ws://127.0.0.1:9725/ws?clientId=${viewClientId}`);

// 연결 완료 시 메시지 전송
socket.onopen = function () {
    console.log("WebSocket 연결 성공");
    socket.send(JSON.stringify({type:'connect',message: "연결성공" }));
};

// 수신 메시지 -> 웹소켓 서버에서 전달 받는 메서드
socket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log(data);
    
};

 

파이썬 클라이언트 부분(sockets.py)

 

먼저 websockets 라이브러리를 다운받아야합니다.

pip install websockets

 

import asyncio
import time
import traceback
from threading import Thread
import websockets
import json

class Socket:
    def __init__(self):        
        self.websocket = None
        self.loop = None ## 이벤트 루프 저장
	
    ## 웹소켓 연결 부분
    async def websocket_client(self,ip,client_id):
        self.loop = asyncio.get_event_loop()
        while True:
            try:
                uri = f"ws://{ip}/ws?clientId={client_id}"  # clientId 쿼리파라미터 추가
                print(uri)
                async with websockets.connect(uri) as websocket:
                    self.websocket = websocket
                    await websocket.send(json.dumps({'type':'connect','message':f"{client_id}입니다."}))

                    while True:
                        message = await websocket.recv() ## 수신된 메시지
                        print(f"Received from server: {message}")
                        try:
                            await self.command(message=message) 
                        except Exception as e:
                            print(e)
            except:
                print("웹소켓 연결 에러::",traceback.format_exc())
                await asyncio.sleep(60)  ## 1분 대기 후 다시 연결
	
    ## 명령 수신
    async def command(self, message):  
        data = json.loads(message)
        print(data)
        

	## 웹소켓 서버에 메시지 전송
    def send_message(self, message: dict):
        """
        외부에서 호출 가능한 동기 함수 (controller에서 사용)
        이벤트 루프에 코루틴을 안전하게 제출한다.
        """
        if self.loop and self.websocket:
            asyncio.run_coroutine_threadsafe(self.send_message_server(message), self.loop)
        else:
            print("⚠️ 이벤트 루프 또는 WebSocket이 아직 준비되지 않았습니다.")

    async def send_message_server(self, message: dict):
        if self.websocket:
            await self.websocket.send(json.dumps(message))
        else:
            print("⚠️ WebSocket이 아직 연결되지 않았습니다.")

 

'Tools & Functions > Project 기능들' 카테고리의 다른 글

SSE 통신(Server Sent Event)  (0) 2024.05.24
[JSP] 메일 인증 기능 구현  (1) 2024.05.19
[JSP] 댓글 기능 구현  (0) 2024.05.19
[JSP] 영상 업로드 기능구현  (0) 2024.05.19
[JSP] 쪽지 기능 구현  (0) 2024.05.19