Q132284591 发表于 2018-12-1 13:02:22

在Java7与Tomcat8环境下使用WebSocket实现聊天的示例

     文中案例在apache-tomcat-8.0.15和jdk1.8.0_25环境下开发,运行。不过标题上是Java7,并不造成影响,代码中没有涉及任何与jdk1.8.x相关的代码。另外之所以要讲清软件版本,一是为了保证案例能够有个明确的实现背景,二是websocket实现tomcat7.x与tomcat8.x有很大差异。
     

  在JavaEE规范集中我们这里主要看Java API for WebSocket(JSR 356)。Websocket-api提供了Java实现Websocket的接口,其中最重要的几个类和注解如下图:
  http://s3.运维网.com/wyfs02/M02/54/32/wKiom1R71HfSnxu9AATraxMbUG0847.jpg
  上图解释:
  1.最上面4个注解OnClose,OnError, OnOpen, OnMessage用来标注一个POJO用来处理WebSocket请求的方法;
  2.Endpoint和EndpointConfig分别定义了端点和端点相关配置的接口方法;
  3.ClientEndpoint和ServerEndpoint分别定义了客户端和服务器端端点的接口方法;
  4.Decoder和Encoder分别是解码和编码的接口方法定义;
  5.Session是与Endpoint相关的WebSocket Session接口方法定义。
  另外还有其它接口,这里作为初步了解Java websocket api仅列出最有必要的一些。
  

     Tomcat8.x提供了JavaEE7的标准实现,其中WebSocket 1.1规范给予实现。在使用其开发的依赖环境是Tomcat8.x JDK7。Tomcat8.x提供了服务器端的实现,客户端实现需要借助其他实现如java_websocket。
  
  Tomcat8.x对WebSocket实现感觉很明朗化了,既然API中定义了WebSocket相关的注解和Session那么Tomcat8.x实现中自然会有相应的处理和实现,下图是简单的关系描述。
   http://s3.运维网.com/wyfs02/M02/54/31/wKioL1R71y-yvpatAAI1MX-cI-o472.jpg

  上图解释:

[*]  tomcat的WsSession类实现了Java WebSocket API中的Session接口
[*]  PojoEndpointBase以及其子类处理与Endpoint相关的类或注解
[*]  EndpointConfig,Endpoint都与Session的实现类之间存在依赖关系
  

     通过了解Java Websocket API和Tomcat8.x对其的实现,认为使用Java WebSocket需要熟悉其中关键类或接口如:Endpoint,EndpointConfig,Client和Server,Encoder和Decoder,Session,MessageHandler。

  

     上面大致介绍了Tomcat8.x对Java Websocket的实现,有关WebSocket规范的了解信息可以参考:
        RFC6455: http://tools.ietf.org/html/rfc6455
     WebSocket服务器端实现:https://java.net/projects/websocket-spec/pages/WebSocketAPIs
     

  下面是通过代码示例来展示基于WebSocket的实时聊天
  1.服务器端实现

  
package t8j8.examples;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/ws/chat/{nickName}")
public class Chat {
    /**
   * 连接对象集合
   */
    private static final Set connections = new CopyOnWriteArraySet();
    private String nickName;
    /**
   * WebSocket Session
   */
    private Session session;
    public Chat() {
    }
    /**
   * 打开连接
   *
   * @param session
   * @param nickName
   */
    @OnOpen
    public void onOpen(Session session,
            @PathParam(value = "nickName") String nickName) {
      this.session = session;
      this.nickName = nickName;
      connections.add(this);
      String message = String.format("System> %s %s", this.nickName,
                " has joined.");
      Chat.broadCast(message);
    }
    /**
   * 关闭连接
   */
    @OnClose
    public void onClose() {
      connections.remove(this);
      String message = String.format("System> %s, %s", this.nickName,
                " has disconnection.");
      Chat.broadCast(message);
    }
    /**
   * 接收信息
   *
   * @param message
   * @param nickName
   */
    @OnMessage
    public void onMessage(String message,
            @PathParam(value = "nickName") String nickName) {
      Chat.broadCast(nickName + ">" + message);
    }
    /**
   * 错误信息响应
   *
   * @param throwable
   */
    @OnError
    public void onError(Throwable throwable) {
      System.out.println(throwable.getMessage());
    }
    /**
   * 发送或广播信息
   *
   * @param message
   */
    private static void broadCast(String message) {
      for (Chat chat : connections) {
            try {
                synchronized (chat) {
                  chat.session.getBasicRemote().sendText(message);
                }
            } catch (IOException e) {
                connections.remove(chat);
                try {
                  chat.session.close();
                } catch (IOException e1) {
                }
                Chat.broadCast(String.format("System> %s %s", chat.nickName,
                        " has bean disconnection."));
            }
      }
    }
}  
  
     说明:

      代码中ServerEndpoint注解的value值中有个nickName的参数占位符,该参数占位符称为路径参数,路径参数可以通过方法参数注解(@PathParam)进行设置并且获取;
      在Endpoint注解的类中OnClose,OnOpen,OnError注解的方法只有一个,OnMessage注解的方法可以有多个这个容易理解因为WebSocket可能处理的信息有text,binary,textStream等。
  

  2.客户端实现
  如果使用Maven的话,可以添加java_websocket jar的依赖。

            org.java-websocket
            Java-WebSocket
            1.3.0
            runtime
        

  如果开发Web客户端则可以选择支持WebSocket的浏览器,使用HTML5技术。
  

  下面是使用java_websocket来实现客户端的代码:

package t8j8.examples.client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Scanner;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_17;
import org.java_websocket.handshake.ServerHandshake;
public class TestTocatWebSocket {
    public static void main(String[] args) throws URISyntaxException {
      String url = "ws://localhost:8080/t8j8/ws/chat/" + args;
      WebSocketClient wc = new WebSocketClient(new URI(url), new Draft_17()) {
            @Override
            public void onOpen(ServerHandshake handshakedata) {
                System.out.println(handshakedata.getHttpStatusMessage());
            }
            @Override
            public void onMessage(String message) {
                System.out.println(message);
            }
            @Override
            public void onError(Exception ex) {
            }
            @Override
            public void onClose(int code, String reason, boolean remote) {
            }
      };
      wc.connect();
      while (true) {
            Scanner scanner = new Scanner(System.in);
            String message = scanner.nextLine();
            if (message.equals("q")) {
                wc.close();
                break;
            }
            scanner.close();
            wc.send(message);
      }
    }
}   说明:
      客户端中要说明的是new Draft_17()对象的创建,通过类名可以得知草案的意思。由于Websocket标准迟迟没有发布,因而之前实现都是依据草案进行,而这里的Draft_17对应的WebSocket版本正是:【"Sec-WebSocket-Version", "13" 】可惜,java_websocket包迟迟没有更新内容,名称出现误解人的地方。
  

  3.开始聊天
  部署服务器端程序,启动客户端程序(需要添加nickName的参数)。
  下面是聊天的截图,分别为Tom和Jack。
  
http://s3.运维网.com/wyfs02/M02/54/33/wKiom1R74rWjTwRAAAENkee9KzI276.jpg
http://s3.运维网.com/wyfs02/M00/54/32/wKioL1R74z6z27UsAAFZ621cRio571.jpg
  
  至此,关于使用Tomcat8和JDK7基于WebSocket实现的聊天示例就结束了。




页: [1]
查看完整版本: 在Java7与Tomcat8环境下使用WebSocket实现聊天的示例