java-ee
WebSockets API
수색…
비고
WebSocket은 단일 TCP 연결을 사용하여 클라이언트와 서버 / 엔드 포인트 간의 통신을 가능하게하는 프로토콜입니다.
WebSocket은 웹 브라우저 및 웹 서버에 구현되도록 설계되었지만 모든 클라이언트 또는 서버 응용 프로그램에서 사용할 수 있습니다.
JSR 356 에서 개발되었고 Java EE 7 사양에 통합 된 웹 소켓 용 Java API에 대한이 항목입니다.
WebSocket 통신 생성하기
WebSocket은 단일 TCP 연결을 통해 양방향 / 양방향 통신 프로토콜을 제공합니다.
- 클라이언트가 WebSocket 요청을 수신하는 서버에 대한 연결을 엽니 다.
- 클라이언트는 URI를 사용하여 서버에 연결합니다.
- 서버는 여러 클라이언트의 요청을 청취 할 수 있습니다.
서버 엔드 포인트
단지와 POJO에 주석으로 당신은 웹 소켓 서버 entpoint를 만들 수 있습니다 @ServerEndpoint
. @OnMessage
는 들어오는 메시지를받는 메소드를 @OnMessage
. @OnOpen
은 피어로부터의 새로운 연결이 수신 될 때 호출 될 메소드를 꾸미기 위해 사용될 수 있습니다. 마찬가지로 @OnClose
로 주석 된 메소드는 연결이 닫힐 때 호출됩니다.
@ServerEndpoint("/websocket")
public class WebSocketServerEndpoint
{
@OnOpen
public void open(Session session) {
System.out.println("a client connected");
}
@OnClose
public void close(Session session) {
System.out.println("a client disconnected");
}
@OnMessage
public void handleMessage(String message) {
System.out.println("received a message from a websocket client! " + message);
}
}
클라이언트 Enpoint
서버 끝점과 @ClientEndpoint
POJO에 @ClientEndpoint
주석을 달아 WebSocket 클라이언트 끝점을 만들 수 있습니다.
@ClientEndpoint
public class WebsocketClientEndpoint {
Session userSession = null;
// in our case i.e. "ws://localhost:8080/myApp/websocket"
public WebsocketClientEndpoint(URI endpointURI) {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, endpointURI);
}
@OnOpen
public void onOpen(Session userSession) {
System.out.println("opening websocket");
this.userSession = userSession;
}
@OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing websocket");
this.userSession = null;
}
@OnMessage
public void onMessage(String message) {
System.out.println("received message: "+ message);
}
public void sendMessage(String message) {
System.out.println("sending message: "+ message);
this.userSession.getAsyncRemote().sendText(message);
}
}
인코더와 디코더 : 객체 지향 웹 소켓
인코더와 디코더 덕분에 JSR 356은 객체 지향 통신 모델을 제공합니다.
메시지 정의
수신 된 모든 메시지가 연결된 모든 세션으로 다시 전송되기 전에 서버에 의해 변환되어야한다고 가정합니다.
public abstract class AbstractMsg {
public abstract void transform();
}
이제 서버가 텍스트 기반 메시지와 정수 기반 메시지라는 두 가지 메시지 유형을 관리한다고 가정 해 보겠습니다.
정수 메시지는 내용 자체를 곱합니다.
public class IntegerMsg extends AbstractMsg {
private Integer content;
public IntegerMsg(int content) {
this.content = content;
}
public Integer getContent() {
return content;
}
public void setContent(Integer content) {
this.content = content;
}
@Override
public void transform() {
this.content = this.content * this.content;
}
}
문자열 메시지 앞에 몇 개의 텍스트가 추가됩니다.
public class StringMsg extends AbstractMsg {
private String content;
public StringMsg(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public void transform() {
this.content = "Someone said: " + this.content;
}
}
인코더 및 디코더
메시지 유형마다 하나의 인코더가 있고 모든 메시지에 대해 하나의 디코더가 있습니다. 인코더는 구현해야한다 Encoder.XXX<Type>
디코더를 구현해야하는 경우 인터페이스를 Decoder.XXX<Type>
.
인코딩은 매우 간단합니다. 메시지에서 encode
메소드는 JSON 형식의 String을 출력해야합니다. 다음은 IntegerMsg
의 예입니다.
public class IntegerMsgEncoder implements Encoder.Text<IntegerMsg> {
@Override
public String encode(IntegerMsg object) throws EncodeException {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("content", object.getContent());
JsonObject jsonObject = builder.build();
return jsonObject.toString();
}
@Override
public void init(EndpointConfig config) {
System.out.println("IntegerMsgEncoder initializing");
}
@Override
public void destroy() {
System.out.println("IntegerMsgEncoder closing");
}
}
StringMsg
클래스와 비슷한 인코딩입니다. 물론, 인코더는 추상 클래스를 통해 인수 분해 될 수 있습니다.
public class StringMsgEncoder implements Encoder.Text<StringMsg> {
@Override
public String encode(StringMsg object) throws EncodeException {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("content", object.getContent());
JsonObject jsonObject = builder.build();
return jsonObject.toString();
}
@Override
public void init(EndpointConfig config) {
System.out.println("StringMsgEncoder initializing");
}
@Override
public void destroy() {
System.out.println("StringMsgEncoder closing");
}
}
디코더는 두 단계로 진행됩니다 : 수신 된 메시지가 willDecode
와 함께 예외 형식에 willDecode
다음 수신 된 원본 메시지를 decode
된 객체로 변환합니다.
공용 클래스 MsgDecoder는 Decoder.Text {
@Override
public AbstractMsg decode(String s) throws DecodeException {
// Thanks to willDecode(s), one knows that
// s is a valid JSON and has the attribute
// "content"
JsonObject json = Json.createReader(new StringReader(s)).readObject();
JsonValue contentValue = json.get("content");
// to know if it is a IntegerMsg or a StringMsg,
// contentValue type has to be checked:
switch (contentValue.getValueType()) {
case STRING:
String stringContent = json.getString("content");
return new StringMsg(stringContent);
case NUMBER:
Integer intContent = json.getInt("content");
return new IntegerMsg(intContent);
default:
return null;
}
}
@Override
public boolean willDecode(String s) {
// 1) Incoming message is a valid JSON object
JsonObject json;
try {
json = Json.createReader(new StringReader(s)).readObject();
}
catch (JsonParsingException e) {
// ...manage exception...
return false;
}
catch (JsonException e) {
// ...manage exception...
return false;
}
// 2) Incoming message has required attributes
boolean hasContent = json.containsKey("content");
// ... proceed to additional test ...
return hasContent;
}
@Override
public void init(EndpointConfig config) {
System.out.println("Decoding incoming message...");
}
@Override
public void destroy() {
System.out.println("Incoming message decoding finished");
}
}
ServerEndPoint
Server EndPoint는 세 가지 주요 차이점이있는 WebSocket 통신 과 매우 흡사합니다.
ServerEndPoint 주석에는
encoders
및decoders
속성이 있습니다.sendText
하지만sendObject
하여 메시지를 보내지 않습니다.OnError 주석이 사용됩니다.
willDecode
동안 오류가 발생하면 여기에서 처리되고 오류 정보가 클라이언트로 다시 전송됩니다.@ServerEndpoint (value = "/ webSocketObjectEndPoint", 디코더 = {MsgDecoder.class}, 인코더 = {StringMsgEncoder.class, IntegerMsgEncoder.class}) public class ServerEndPoint {
@OnOpen public void onOpen(Session session) { System.out.println("A session has joined"); } @OnClose public void onClose(Session session) { System.out.println("A session has left"); } @OnMessage public void onMessage(Session session, AbstractMsg message) { if (message instanceof IntegerMsg) { System.out.println("IntegerMsg received!"); } else if (message instanceof StringMsg) { System.out.println("StringMsg received!"); } message.transform(); sendMessageToAllParties(session, message); } @OnError public void onError(Session session, Throwable throwable) { session.getAsyncRemote().sendText(throwable.getLocalizedMessage()); } private void sendMessageToAllParties(Session session, AbstractMsg message) { session.getOpenSessions().forEach(s -> { s.getAsyncRemote().sendObject(message); }); }
}
매우 장황했기 때문에 여기에 시각적 인 예제가 필요한 사람들을위한 기본 JavaScript 클라이언트가 있습니다. 이것은 채팅과 같은 예입니다. 연결된 모든 당사자가 답변을 받게됩니다.
<!DOCTYPE html>
<html>
<head>
<title>Websocket-object</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- start of BAD PRACTICE! all style and script must go into a
dedicated CSS / JavaScript file-->
<style>
body{
background: dimgray;
}
.container{
width: 100%;
display: flex;
}
.left-side{
width: 30%;
padding: 2%;
box-sizing: border-box;
margin: auto;
margin-top: 0;
background: antiquewhite;
}
.left-side table{
width: 100%;
border: 1px solid black;
margin: 5px;
}
.left-side table td{
padding: 2px;
width: 50%;
}
.left-side table input{
width: 100%;
box-sizing: border-box;
}
.right-side{
width: 70%;
background: floralwhite;
}
</style>
<script>
var ws = null;
window.onload = function () {
// replace the 'websocket-object' with the
// context root of your web application.
ws = new WebSocket("ws://localhost:8080/websocket-object/webSocketObjectEndPoint");
ws.onopen = onOpen;
ws.onclose = onClose;
ws.onmessage = onMessage;
};
function onOpen() {
printText("", "connected to server");
}
function onClose() {
printText("", "disconnected from server");
}
function onMessage(event) {
var msg = JSON.parse(event.data);
printText("server", JSON.stringify(msg.content));
}
function sendNumberMessage() {
var content = new Number(document.getElementById("inputNumber").value);
var json = {content: content};
ws.send(JSON.stringify(json));
printText("client", JSON.stringify(json));
}
function sendTextMessage() {
var content = document.getElementById("inputText").value;
var json = {content: content};
ws.send(JSON.stringify(json));
printText("client", JSON.stringify(json));
}
function printText(sender, text) {
var table = document.getElementById("outputTable");
var row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
switch (sender) {
case "client":
row.style.color = "orange";
break;
case "server":
row.style.color = "green";
break;
default:
row.style.color = "powderblue";
}
cell1.innerHTML = new Date().toISOString();
cell2.innerHTML = sender;
cell3.innerHTML = text;
}
</script>
<!-- end of bad practice -->
</head>
<body>
<div class="container">
<div class="left-side">
<table>
<tr>
<td>Enter a text</td>
<td><input id="inputText" type="text" /></td>
</tr>
<tr>
<td>Send as text</td>
<td><input type="submit" value="Send" onclick="sendTextMessage();"/></td>
</tr>
</table>
<table>
<tr>
<td>Enter a number</td>
<td><input id="inputNumber" type="number" /></td>
</tr>
<tr>
<td>Send as number</td>
<td><input type="submit" value="Send" onclick="sendNumberMessage();"/></td>
</tr>
</table>
</div>
<div class="right-side">
<table id="outputTable">
<tr>
<th>Date</th>
<th>Sender</th>
<th>Message</th>
</tr>
</table>
</div>
</div>
</body>
</html>
코드가 완성되어 Payara 4.1에서 테스트되었습니다. 예제는 순수 표준입니다 (외부 라이브러리 / 프레임 워크 없음).