软件编程
位置:首页>> 软件编程>> java编程>> JavaEE7+Websockets+GlassFish4打造聊天室

JavaEE7+Websockets+GlassFish4打造聊天室

作者:lijiao  发布时间:2023-11-29 01:01:39 

标签:Java,JavaEE7,聊天室

在客户机和服务器之间建立单一的双向连接,这就意味着客户只需要发送一个请求到服务端,那么服务端则会进行处理,处理好后则将其返回给客户端,客户端则可以在等待这个时间继续去做其他工作,整个过程是异步的。在本系列教程中,将指导用户如何在JAVA EE7的容器GlassFish 4中,使用JAVA EE 7中的全新的解析Json API(JSR-353),以及综合运用jQuery和Bootstrap。本文要求读者有一定的HTML 5 Websocket的基础原理知识。

效果图

我们先来看下在完成这个教程后的效果图,如下所示:

JavaEE7+Websockets+GlassFish4打造聊天室

准备工作

我们使用的是JDK 7 和MAVN 3进行库的构建工作,首先看pom.xml中关于Jave EE 7的部分:


<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
 <groupId>javax</groupId>
 <artifactId>javaee-api</artifactId>
 <version>7.0</version>
 <scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.1</version>
  <configuration>
   <source>1.7</source>
   <target>1.7</target>
   <compilerArguments>
    <endorseddirs>${endorsed.dir}</endorseddirs>
   </compilerArguments>
  </configuration>
 </plugin>
 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-war-plugin</artifactId>
  <version>2.3</version>
  <configuration>
   <failOnMissingWebXml>false</failOnMissingWebXml>
  </configuration>
 </plugin>
 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>2.6</version>
  [..]
 </plugin>
</plugins>
</build>

同时,为了能使用GlassFish 4,需要增加如下的插件:


plugin>
<groupId>org.glassfish.embedded</groupId>
<artifactId>maven-embedded-glassfish-plugin</artifactId>
<version>4.0</version>
<configuration>
 <goalPrefix>embedded-glassfish</goalPrefix>
 <app>${basedir}/target/${project.artifactId}-${project.version}.war</app>
 <autoDelete>true</autoDelete>
 <port>8080</port>
 <name>${project.artifactId}</name>
 <contextRoot>hascode</contextRoot>
</configuration>
<executions>
 <execution>
  <goals>
   <goal>deploy</goal>
  </goals>
 </execution>
</executions>
</plugin>

设置Websocket的Endpoint

我们先来看服务端Websocket的代码如下,然后再做进一步解析:


package com.hascode.tutorial;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.websocket.EncodeException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/chat/{room}", encoders = ChatMessageEncoder.class, decoders = ChatMessageDecoder.class)
public class ChatEndpoint {
private final Logger log = Logger.getLogger(getClass().getName());

@OnOpen
public void open(final Session session, @PathParam("room") final String room) {
 log.info("session openend and bound to room: " + room);
 session.getUserProperties().put("room", room);
}

@OnMessage
public void onMessage(final Session session, final ChatMessage chatMessage) {
 String room = (String) session.getUserProperties().get("room");
 try {
  for (Session s : session.getOpenSessions()) {
   if (s.isOpen()
     && room.equals(s.getUserProperties().get("room"))) {
    s.getBasicRemote().sendObject(chatMessage);
   }
  }
 } catch (IOException | EncodeException e) {
  log.log(Level.WARNING, "onMessage failed", e);
 }
}
}

下面分析下上面的代码:

使用@ ServerEndpoint定义一个新的endpoint,其中的值指定了URL并且可以使用PathParams参数,就象在JAX-RS中的用法一样。

所以值“/chat/{room}”允许用户通过如下形式的URL去连接某个聊天室:ws://0.0.0.0:8080/hascode/chat/java

在大括号中的值(即room),可以通过使用javax.websocket.server.PathParam,在endpoint的生命周期回调方法中以参数的方式注入。

此外,我们要使用一个编码和解码的类,因为我们使用的是一个DTO形式的类,用于在服务端和客户端传送数据。

当用户第一次连接到服务端,输入要进入聊天室的房号,则这个房号以参数的方式注入提交,并且使用session.getUserProperties将值保存在用户的属性map中。

当一个聊天参与者通过tcp连接发送信息到服务端,则循环遍历所有已打开的session,每个session被绑定到指定的聊天室中,并且接收编码和解码的信息。

如果我们想发送简单的文本信息或和二进制格式的信息,则可以使用session.getBasicRemote().sendBinary() 或session.getBasicRemote().sendText()

接下来我们看下用于代表信息传递实体(DTO:Data Transfer Object)的代码,如下:


package com.hascode.tutorial;

import java.util.Date;

public class ChatMessage {
private String message;
private String sender;
private Date received;

// 其他getter,setter方法
}

聊天消息的转换

在这个应用中,将编写一个编码和解码类,用于在聊天信息和JSON格式间进行转换。

先来看下解码类的实现,这将会把传递到服务端的聊天信息转换为ChatMessage实体类。在这里,使用的是Java API for JSON Processing(JSR353)规范去将JSON格式的信息转换为实体类,代码如下,其中重写的willDecode方法,这里默认返回为true。


package com.hascode.tutorial;

import java.io.StringReader;
import java.util.Date;

import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

public class ChatMessageDecoder implements Decoder.Text<ChatMessage> {
@Override
public void init(final EndpointConfig config) {
}

@Override
public void destroy() {
}

@Override
public ChatMessage decode(final String textMessage) throws DecodeException {
 ChatMessage chatMessage = new ChatMessage();
 JsonObject obj = Json.createReader(new StringReader(textMessage))
   .readObject();
 chatMessage.setMessage(obj.getString("message"));
 chatMessage.setSender(obj.getString("sender"));
 chatMessage.setReceived(new Date());
 return chatMessage;
}

@Override
public boolean willDecode(final String s) {
 return true;
}
}

同样再看下编码类的代码,这个类相反,是将ChatMessage类转换为Json格式,代码如下:


package com.hascode.tutorial;

import javax.json.Json;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public class ChatMessageEncoder implements Encoder.Text<ChatMessage> {
@Override
public void init(final EndpointConfig config) {
}

@Override
public void destroy() {
}

@Override
public String encode(final ChatMessage chatMessage) throws EncodeException {
 return Json.createObjectBuilder()
   .add("message", chatMessage.getMessage())
   .add("sender", chatMessage.getSender())
   .add("received", chatMessage.getReceived().toString()).build()
   .toString();
}
}

这里可以看到JSR-353的强 * ,只需要调用Json.createObjectBuilder就可以轻易把一个DTO对象转化为JSON了。

通过Bootstrap、Javacsript搭建简易客户端

最后,我们综合运用著名的Bootstrap、jQuery框架和Javascript设计一个简易的客户端。我们在src/main/weapp目录下新建立index.html文件,代码如下:


<!DOCTYPE html>
<html lang="en">
<head>
[..]
<script>
var wsocket;
var serviceLocation = "ws://0.0.0.0:8080/hascode/chat/";
var $nickName;
var $message;
var $chatWindow;
var room = '';

function onMessageReceived(evt) {
 //var msg = eval('(' + evt.data + ')');
 var msg = JSON.parse(evt.data); // native API
 var $messageLine = $('<tr><td class="received">' + msg.received
   + '</td><td class="user label label-info">' + msg.sender
   + '</td><td class="message badge">' + msg.message
   + '</td></tr>');
 $chatWindow.append($messageLine);
}
function sendMessage() {
 var msg = '{"message":"' + $message.val() + '", "sender":"'
   + $nickName.val() + '", "received":""}';
 wsocket.send(msg);
 $message.val('').focus();
}

function connectToChatserver() {
 room = $('#chatroom option:selected').val();
 wsocket = new WebSocket(serviceLocation + room);
 wsocket.onmessage = onMessageReceived;
}

function leaveRoom() {
 wsocket.close();
 $chatWindow.empty();
 $('.chat-wrapper').hide();
 $('.chat-signin').show();
 $nickName.focus();
}

$(document).ready(function() {
 $nickName = $('#nickname');
 $message = $('#message');
 $chatWindow = $('#response');
 $('.chat-wrapper').hide();
 $nickName.focus();

$('#enterRoom').click(function(evt) {
  evt.preventDefault();
  connectToChatserver();
  $('.chat-wrapper h2').text('Chat # '+$nickName.val() + "@" + room);
  $('.chat-signin').hide();
  $('.chat-wrapper').show();
  $message.focus();
 });
 $('#do-chat').submit(function(evt) {
  evt.preventDefault();
  sendMessage()
 });

$('#leave-room').click(function(){
  leaveRoom();
 });
});
</script>
</head>

<body>

<div class="container chat-signin">
 <form class="form-signin">
  <h2 class="form-signin-heading">Chat sign in</h2>
  <label for="nickname">Nickname</label> <input type="text"
   class="input-block-level" placeholder="Nickname" id="nickname">
  <div class="btn-group">
   <label for="chatroom">Chatroom</label> <select size="1"
    id="chatroom">
    <option>arduino</option>
    <option>java</option>
    <option>groovy</option>
    <option>scala</option>
   </select>
  </div>
  <button class="btn btn-large btn-primary" type="submit"
   id="enterRoom">Sign in</button>
 </form>
</div>
<!-- /container -->

<div class="container chat-wrapper">
 <form id="do-chat">
  <h2 class="alert alert-success"></h2>
  <table id="response" class="table table-bordered"></table>
  <fieldset>
   <legend>Enter your message..</legend>
   <div class="controls">
    <input type="text" class="input-block-level" placeholder="Your message..." id="message" style="height:60px"/>
    <input type="submit" class="btn btn-large btn-block btn-primary"
     value="Send message" />
    <button class="btn btn-large btn-block" type="button" id="leave-room">Leave
     room</button>
   </div>
  </fieldset>
 </form>
</div>
</body>
</html>

在上面的代码中,要注意如下几点:

在Javascript端要调用websocket的话,要用如下的方式发起连接即可:ws://IP:PORT/CONTEXT_PATH/ENDPOINT_URL e.g ws://0.0.0.0:8080/hascode/chat/java

创建一个Websocket连接的方法很简单,使用的是var wsocket = new WebSocket(‘ws://0.0.0.0:8080/hascode/chat/java');

要获得来自服务端返回的信息,只需要在回调函数wsocket.onmessage中设置对应的获取返回信息的方法即可。

发送一个Websocket消息到服务端,使用的方法是wsocket.send(),其中可以发送的消息可以文本或者二进制数据。

关闭连接使用的是wsocket.close()。

最后,我们通过mvn package embedded-glassfish:run进行代码的部署,然后就可以看到本文开始部分截图的效果。

JavaEE7+Websockets+GlassFish4打造聊天室

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com