참고:
[NodeJS][SocketIO]SocketIO 기초 사용하기, 단일 통신, server,client-(1)
long polling으로 채팅구현 2탄이다.
롱풀링에 대한 설명은 위의 참고의 세번째, 네번째 포스팅들을 참고하시고 이번엔 구현에 초점을 맞춰보자.
사실 요즘 JSP는 유지보수가 아니면 학습용으로 밖에 안쓰이므로 의미가 있나 싶긴한데
그래도 내 블로그를 찾는 많은 여러분들이 학생분이니까 JSP도 준비했다.
역시 옜날 플랫폼이라서 토악질 나오는 코드의 양이 되었는데 그래도 여러분들이 잘 따라와줬으면 좋겠다.
일반적으로 채팅구현은 웹소켓이 더 낫기 때문에 롱풀링보다는 소켓을 알아보는게 더 도움은 될 것이다.
<%--chat.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Long polling chatting</title>
<link rel='stylesheet' href='/stylesheets/style.css'/>
<script src="https://npmcdn.com/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<textarea cols="40" rows="40" id="chatArea"></textarea><br>
<input type="text" name="chatText" id="test">
<input type="button" value="보내기" onclick="reqMsg()">
<script>
function reqMsg() {
let chatText = $(`[name="chatText"]`);
let sendData = chatText.val();
chatText.val('');
console.log(sendData);
axios.post('http://localhost:8080/msg', {sendData})
.then((res) => {
console.log('post success');
})
.catch((res) => {
console.log('post fail');
})
}
function resMsg() {
axios.get('http://localhost:8080/msg')
.then((res) => {
console.log('get success');
if(res.data.data!==undefined){
let chatArea = $('#chatArea');
chatArea.val(chatArea.val() + res.data.data + '\n');
}
resMsg();
})
.catch((res) => {
console.log('get fail');
setTimeout(resMsg, 5000);
})
}
resMsg();
</script>
</body>
</html>
사실 뭐 크게 설명할 건 없다. 사실 nodejs버전에서 설명해서 좀 귀찮다.
그래도 귀찮음을 감내하고 그냥 설명하도록 하겠다.
function reqMsg() {
let chatText = $(`[name="chatText"]`);
let sendData = chatText.val();
chatText.val('');
console.log(sendData);
axios.post('http://localhost:8080/msg', {sendData})
.then((res) => {
console.log('post success');
})
.catch((res) => {
console.log('post fail');
})
}
보내는 예제다. 보내니까 post를 쓴다. 그냥 별거 없는 예제이다.
function resMsg() {
axios.get('http://localhost:8080/msg')
.then((res) => {
console.log('get success');
if (res.data.data !== undefined) {
let chatArea = $('#chatArea');
chatArea.val(chatArea.val() + res.data.data + '\n');
}
resMsg();
})
.catch((res) => {
console.log('get fail');
setTimeout(resMsg, 5000);
})
}
이건 받는 예제이다. 조금 코드가 독특한데 성공하건 실패하건 값을 다시 요청하게 된다.
그리고 값을 받았는데 그 값이 undefiend가 아니라면 화면에 새로 그리게된다.
클라이언트쪽 코드를 봤으니 서버쪽도 보도록하자.
//MsgServlet.java
package example;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@WebServlet(urlPatterns = {"/msg"}, asyncSupported = true)
public class MsgServlet extends HttpServlet {
private static List<AsyncContext> contexts = new LinkedList<>();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<AsyncContext> asyncContexts = new ArrayList<>(MsgServlet.contexts);
MsgServlet.contexts.clear();
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(new InputStreamReader(request.getInputStream()));
JsonElement chat = jsonObject.get("sendData");
JsonObject respData = new JsonObject();
respData.add("data", chat);
String data = respData.toString();
System.out.println(data);
for (AsyncContext asyncContext : asyncContexts) {
try (PrintWriter out = asyncContext.getResponse().getWriter()) {
asyncContext.getResponse().setContentType("application/json");
out.println(data);
out.flush();
asyncContext.complete();
} catch (Exception e) {
/*pass*/
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(5000);
asyncContext.addListener(new MyAsyncListener());
contexts.add(asyncContext);
}
}
class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
asyncEvent.getAsyncContext().complete();
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
}
}
서버쪽 코드도 별로 길지는 않나? 모르겠다.
로직만 보면 길지는 않으니 설명하도록하겠다.
private static List<AsyncContext> contexts = new LinkedList<>();
일단 우리는 비동기로 처리할것이므로 AsyncContext로 호출한다.
일반 response는 담아봤자 의미없다. 반드시 AsyncContext화 시켜줘야한다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(5000);
asyncContext.addListener(new MyAsyncListener());
contexts.add(asyncContext);
}
Get부분을 보면 알겠지만 먼저 들어온 request처리를 비동기로 바꿔준다.
시간제한은 여러분이 원하는 시간으로 하면된다. 사실은 길면 길수록 좋다.
그리고 리스너를 달아서 감지해주는데 사실 timeout이 아주 길면 필요없긴한데 우리는 고작 5초이므로 달아준다.
그리고 마지막에 비동기 컨텍스트 배열에 담아준다.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<AsyncContext> asyncContexts = new ArrayList<>(MsgServlet.contexts);
MsgServlet.contexts.clear();
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(new InputStreamReader(request.getInputStream()));
JsonElement chat = jsonObject.get("sendData");
JsonObject respData = new JsonObject();
respData.add("data", chat);
String data = respData.toString();
System.out.println(data);
for (AsyncContext asyncContext : asyncContexts) {
try (PrintWriter out = asyncContext.getResponse().getWriter()) {
asyncContext.getResponse().setContentType("application/json");
out.println(data);
out.flush();
asyncContext.complete();
} catch (Exception e) {
/*pass*/
}
}
}
이제 post쪽 코드이다. 마찬가지로 코드가 길어보이지만 사실 내용이... 많네.
List<AsyncContext> asyncContexts = new ArrayList<>(MsgServlet.contexts);
MsgServlet.contexts.clear();
이제 일괄 처리를 할것이므로 배열을 복제해서 넣어준다.
사실 이렇게 까지 할필요가 없을 수도 있지만 비워주는 작업은 반드시 필요하다.
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(new InputStreamReader(request.getInputStream()));
JsonElement chat = jsonObject.get("sendData");
JsonObject respData = new JsonObject();
respData.add("data", chat);
String data = respData.toString();
이 부분에서 설명이 좀 필요할 것같다.
먼저 우리는 데이터를 json으로 받았는데 보낼때 form의 형태를 application/x-www-form-urlencode형태가 아니라 application/json형태로 보냈다.
그래서 json으로 넘어왔는데 구식 jsp는 이를 캐칭하지 못한다.
그리서 기존의 getParameter가 아닌 getInputStream을 호출하고 이를 Reader로 만든뒤 jsonParser로 파싱하여 json데이터로 만든다.
이 json은 gson으로 현재 업계 1위 자바 라이브러리이다.
이 포스팅과 이 포스팅에 어느정도 사용법이 나오므로 참고하길 바란다. 라이브러리를 추가하는 방법은 eclipse의 경우 이 포스팅을 참조하자.
어쨋든 gson으로 json형태로 만든뒤 sendData를 받아서 새로운 js object를 만들고 거기에 data를 이름으로 넣는다.
이러면 준비 끝.
for (AsyncContext asyncContext : asyncContexts) {
try (PrintWriter out = asyncContext.getResponse().getWriter()) {
asyncContext.getResponse().setContentType("application/json");
out.println(data);
out.flush();
asyncContext.complete();
} catch (Exception e) {
/*pass*/
}
}
이제 순회하면서 데이터 뿌려주면 끝난다.
'Usage > Java-JSP-Tomcat' 카테고리의 다른 글
[JSP]윈도우에서 톰캣 설치와 톰캣 서버 구동 (1) | 2021.03.04 |
---|---|
[JSP][tomcat]톰캣 포트번호 변경하기 (0) | 2019.04.06 |
[JSP]Servlet 비동기로 사용하기(AsyncContext) (0) | 2018.12.29 |
[JSP]eclipse프로젝트를 intellij에서 실행하기 (0) | 2017.12.30 |
[JSP]JSP파일 인코딩 변환 (0) | 2017.12.28 |