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 |