안녕하세요. 명월입니다.
이 글은 Java에서 WebSocket의 Session 사용 방법(Broadcast)과 웹 채팅 소스 예제에 대한 글입니다.
이전에 제가 WebSocket 설정, 브라우저에서 접속, 메시지 전송 등에 대해 설명한 적이 있습니다.
링크 - [Java] Servlet에서 사용하는 웹 소켓 (WebSocket)
CS프로그램의 소켓 통신이면 서버 소켓에서 클라이언트가 접속을 하게 되면 ClientSocket 인스턴스를 받습니다.
그 ClientSocket 인스턴스로 연결을 유지(통신 동기화)하고 서버에서 클라이언트로 메시지를 보내게 됩니다.
WebSocket에서는 Session의 객체가 있습니다. 이 Session의 객체는 브라우저로 부터 Socket 접속을 하면 생성이 되고, 이 객체를 리스트등에 관리하면서 필요하면 꺼내서 메시지를 보낼 수도 있습니다.
이 Session은 웹 Session(서버에 클라이언트 별 정보를 저장하는 오브젝트)과는 의미가 다릅니다. 웹 세션은 브라우저가 Web Request를 할 때 마다, 쿠키의 세션 키로 구분하여 유저의 정보를 가져오는 것을 말합니다.
여기서 소켓 세션은 브라우저가 Websocket을 접속했을 때의 커넥션 정보가 있는 것입니다.CS프로그램이라고 하면 ClientSocket과 비슷한 객체입니다.
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
// WebSocket 호스트 설정
@ServerEndpoint("/broadsocket")
public class BroadSocket {
// 접속 된 클라이언트 WebSocket session 관리 리스트
private static List<Session> sessionUsers = Collections.synchronizedList(new ArrayList<>());
// 메시지에서 유저 명을 취득하기 위한 정규식 표현
private static Pattern pattern = Pattern.compile("^\\{\\{.*?\\}\\}");
// WebSocket으로 브라우저가 접속하면 요청되는 함수
@OnOpen
public void handleOpen(Session userSession) {
// 클라이언트가 접속하면 WebSocket세션을 리스트에 저장한다.
sessionUsers.add(userSession);
// 콘솔에 접속 로그를 출력한다.
System.out.println("client is now connected...");
}
// WebSocket으로 메시지가 오면 요청되는 함수
@OnMessage
public void handleMessage(String message, Session userSession) throws IOException {
// 메시지 내용을 콘솔에 출력한다.
System.out.println(message);
// 초기 유저 명
String name = "anonymous";
// 메시지로 유저 명을 추출한다.
Matcher matcher = pattern.matcher(message);
// 메시지 예: {{유저명}}메시지
if (matcher.find()) {
name = matcher.group();
}
// 클로져를 위해 변수의 상수화
final String msg = message.replaceAll(pattern.pattern(), "");
final String username = name.replaceFirst("^\\{\\{", "").replaceFirst("\\}\\}$", "");
// session관리 리스트에서 Session을 취득한다.
sessionUsers.forEach(session -> {
// 리스트에 있는 세션과 메시지를 보낸 세션이 같으면 메시지 송신할 필요없다.
if (session == userSession) {
return;
}
try {
// 리스트에 있는 모든 세션(메시지 보낸 유저 제외)에 메시지를 보낸다. (형식: 유저명 => 메시지)
session.getBasicRemote().sendText(username + " => " + msg);
} catch (IOException e) {
// 에러가 발생하면 콘솔에 표시한다.
e.printStackTrace();
}
});
}
// WebSocket과 브라우저가 접속이 끊기면 요청되는 함수
@OnClose
public void handleClose(Session userSession) {
// session 리스트로 접속 끊은 세션을 제거한다.
sessionUsers.remove(userSession);
// 콘솔에 접속 끊김 로그를 출력한다.
System.out.println("client is now disconnected...");
}
}
예전에 설명한 WebSocket 리스너 구조와 같습니다. 그러나 이번에는 각 리스너에 Session 파라미터를 추가했습니다.
Session 은 WebSocket의 커넥션 정보가 들어있는 인스턴스입니다.
여기서 브라우저로 부터 메시지를 받으면 메시지 안에서 유저명을 추출하여 다른 유저들에게 메시지를 보내는 형식으로 되어 있습니다.
메시지의 형식은 「{{유저명}}메시지」의 형태로 되어있습니다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><title>Web Socket Example</title></head>
<body>
<form>
<!-- 유저 명을 입력하는 텍스트 박스(기본 값은 anonymous(익명)) -->
<input id="user" type="text" value="anonymous">
<!-- 송신 메시지를 작성하는 텍스트 박스 -->
<input id="textMessage" type="text">
<!-- 메세지를 송신하는 버튼 -->
<input onclick="sendMessage()" value="Send" type="button">
<!-- WebSocket 접속 종료하는 버튼 -->
<input onclick="disconnect()" value="Disconnect" type="button">
</form>
<br />
<!-- 콘솔 메시지의 역할을 하는 로그 텍스트 에리어.(수신 메시지도 표시한다.) -->
<textarea id="messageTextArea" rows="10" cols="50"></textarea>
<script type="text/javascript">
// 「WebSocketEx」는 프로젝트 명
// 「broadsocket」는 호스트 명
// WebSocket 오브젝트 생성 (자동으로 접속 시작한다. - onopen 함수 호출)
var webSocket = new WebSocket("ws://localhost:8080/WebSocketEx/broadsocket");
// 콘솔 텍스트 에리어 오브젝트
var messageTextArea = document.getElementById("messageTextArea");
// WebSocket 서버와 접속이 되면 호출되는 함수
webSocket.onopen = function(message) {
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += "Server connect...\n";
};
// WebSocket 서버와 접속이 끊기면 호출되는 함수
webSocket.onclose = function(message) {
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += "Server Disconnect...\n";
};
// WebSocket 서버와 통신 중에 에러가 발생하면 요청되는 함수
webSocket.onerror = function(message) {
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += "error...\n";
};
/// WebSocket 서버로 부터 메시지가 오면 호출되는 함수
webSocket.onmessage = function(message) {
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += message.data + "\n";
};
// Send 버튼을 누르면 호출되는 함수
function sendMessage() {
// 유저명 텍스트 박스 오브젝트를 취득
var user = document.getElementById("user");
// 송신 메시지를 작성하는 텍스트 박스 오브젝트를 취득
var message = document.getElementById("textMessage");
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += user.value + "(me) => " + message.value + "\n";
// WebSocket 서버에 메시지를 전송(형식 「{{유저명}}메시지」)
webSocket.send("{{" + user.value + "}}" + message.value);
// 송신 메시지를 작성한 텍스트 박스를 초기화한다.
message.value = "";
}
// Disconnect 버튼을 누르면 호출되는 함수
function disconnect() {
// WebSocket 접속 해제
webSocket.close();
}
</script>
</body>
</html>
WebSocket서버에 메시지를 전송할 때의 메시지 형태는 「{{유저 명}}메시지」의 형태로 만들어서 보냅니다. 그것은 서버의 handleMessage 함수에서 정규 표현식으로 유저명과 메시지 내용을 분리하는 것입니다.
그럼 위 소스를 톰켓으로 기동하겠습니다.
기동한 후에 브라우저를 3개 실행하여 접속했습니다. 유저명은 각 「test1」,「test2」,「test3」으로 설정하여 메시지를 보냈습니다.
「test1」에서 보낸 메시지가 「test2」、「test3」의 브라우저에도 보입니다. 이렇게 간단하게 채팅 프로그램이 만들어 졌습니다.
서버 측의 콘솔 로그를 확인했습니다. 클라이언트가 3개 접속해서 각각 메시지를 보낸 것을 확인할 수 있습니다.
-----추가 2020년 6월 3일-----
질문 중에 Websocket이 끊기면 다시 재접속하는 방법에 대한 질문이 있어서 작성합니다.
재 접속을 하는 방법은 onclose이벤트에 다시 new WebSocket을 선언하면 가능합니다. 그런데 다시 선언할 때 주의할 점이 이밴트를 다시 설정해야 합니다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Web Socket Example</title>
</head>
<body>
<form>
<!-- 유저 명을 입력하는 텍스트 박스(기본 값은 anonymous(익명)) -->
<input id="user" type="text" value="anonymous">
<!-- 송신 메시지를 작성하는 텍스트 박스 -->
<input id="textMessage" type="text">
<!-- 메세지를 송신하는 버튼 -->
<input onclick="sendMessage()" value="Send" type="button">
<!-- WebSocket 접속 종료하는 버튼 -->
<input onclick="disconnect()" value="Disconnect" type="button">
</form>
<br />
<!-- 콘솔 메시지의 역할을 하는 로그 텍스트 에리어.(수신 메시지도 표시한다.) -->
<textarea id="messageTextArea" rows="10" cols="50"></textarea>
<script type="text/javascript">
// 콘솔 텍스트 에리어 오브젝트
var messageTextArea = document.getElementById("messageTextArea");
// 웹 소켓 접속 함수, url 뒤의 파라미터는 callback 함수를 받는다.
function connectWebSocket(url, message, open, close, error) {
// WebSocket 오브젝트 생성 (자동으로 접속 시작한다. - onopen 함수 호출)
let webSocket = new WebSocket(url);
// 함수 체크하는 함수
function call(cb, msg) {
// cb가 함수 타입인지 확인
if (cb !== undefined && typeof cb === "function") {
// 함수 호출
cb.call(null, msg);
}
}
// WebSocket 서버와 접속이 되면 호출되는 함수
webSocket.onopen = function() {
// callback 호출
call(open);
};
// WebSocket 서버와 접속이 끊기면 호출되는 함수
webSocket.onclose = function() {
// callback 호출
call(close);
};
// WebSocket 서버와 통신 중에 에러가 발생하면 요청되는 함수
webSocket.onerror = function() {
// callback 호출
call(error);
};
// WebSocket 서버로 부터 메시지가 오면 호출되는 함수
webSocket.onmessage = function(msg) {
// callback 호출
call(message, msg);
};
// 웹 소켓 리턴
return webSocket;
}
// 연결 발생 때 사용할 callback 함수
var open = function() {
// 콘솔 텍스트에 메시지를 출력한다
messageTextArea.value += "Server connect...\n";
}
// 종료 발생 때 사용할 callback 함수
var close = function() {
// 콘솔 텍스트에 메시지를 출력한다
messageTextArea.value += "Server Disconnect...\n";
// 재 접속을 시도한다.
setTimeout(function() {
// 재접속
webSocket = connectWebSocket("ws://localhost:8080/WebSocket/broadsocket", message, open, close, error);
});
}
// 에러 발생 때 사용할 callback 함수
var error = function() {
messageTextArea.value += "error...\n";
}
// 메세지를 받을 때 사용할 callback 함수
var message = function(msg) {
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += msg.data + "\n";
};
// 웹 소켓 생성
var webSocket = connectWebSocket("ws://localhost:8080/WebSocket/broadsocket", message, open, close, error);
// Send 버튼을 누르면 호출되는 함수
function sendMessage() {
// 유저명 텍스트 박스 오브젝트를 취득
var user = document.getElementById("user");
// 송신 메시지를 작성하는 텍스트 박스 오브젝트를 취득
var message = document.getElementById("textMessage");
// 콘솔 텍스트에 메시지를 출력한다.
messageTextArea.value += user.value + "(me) => " + message.value + "\n";
// WebSocket 서버에 메시지를 전송(형식 「{{유저명}}메시지」)
webSocket.send("{{" + user.value + "}}" + message.value);
// 송신 메시지를 작성한 텍스트 박스를 초기화한다.
message.value = "";
}
// Disconnect 버튼을 누르면 호출되는 함수
function disconnect() {
// WebSocket 접속 해제
webSocket.close();
}
</script>
</body>
</html>
접속 종료 버튼을 눌러도 WebSocket이 다시 접속되는 것을 확인할 수 있습니다.
여기까지 Java에서 WebSocket의 Session 사용 방법(Broadcast)과 웹 채팅 소스 예제에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.
'Development note > Java' 카테고리의 다른 글
[Java] Eclipse에서 Junit을 사용하는 방법 (0) | 2020.06.10 |
---|---|
[Java] Cassandra(nosql)를 사용하는 방법 (0) | 2020.06.08 |
[Java] Selenium을 사용하는 방법 (2) | 2020.06.04 |
[Java] Websocket을 이용해서 유저(사이트 운영자)가 다른 유저와 채팅하는 방법 (0) | 2020.05.06 |
[Java] Servlet에서 사용하는 웹 소켓 (WebSocket) (6) | 2020.02.20 |
[Java] FTP에 접속하여 파일 다운로드, 업로드하는 방법(FTPClient) (0) | 2020.02.18 |
[Java] NIO(Non-Blocking IO) Socket 통신 (0) | 2020.02.04 |
[Java] Mail 발송 (8) | 2020.02.03 |