在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的接口,其中最重要的几个类和注解如下图:

  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实现中自然会有相应的处理和实现,下图是简单的关系描述。

 wKioL1R71y-yvpatAAI1MX-cI-o472.jpg

上图解释:

  1. tomcat的WsSession类实现了Java WebSocket API中的Session接口

  2. PojoEndpointBase以及其子类处理与Endpoint相关的类或注解

  3. 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.服务器端实现

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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<Chat> connections =  new  CopyOnWriteArraySet<Chat>();
 
     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的依赖。

1
2
3
4
5
6
< dependency >
             < groupId >org.java-websocket</ groupId >
             < artifactId >Java-WebSocket</ artifactId >
             < version >1.3.0</ version >
             < scope >runtime</ scope >
         </ dependency >


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


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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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[ 0 ];
         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。

 

wKiom1R74rWjTwRAAAENkee9KzI276.jpg

wKioL1R74z6z27UsAAFZ621cRio571.jpg

 

至此,关于使用Tomcat8和JDK7基于WebSocket实现的聊天示例就结束了。



本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1584957,如需转载请自行联系原作者

相关文章
|
2月前
|
存储 JavaScript 前端开发
webSocket+Node+Js实现在线聊天(包含所有代码)
文章介绍了如何使用WebSocket、Node.js和JavaScript实现在线聊天功能,包括完整的前端和后端代码示例。
160 0
|
3月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
57 1
|
3月前
|
Java 应用服务中间件 Windows
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
|
1月前
|
网络协议 Java 应用服务中间件
Tomcat中的WebSocket是如何实现的?
【10月更文挑战第7天】本文介绍了WebSocket在Tomcat中的实现,包括其全双工通信、单个TCP连接、协议升级和事件驱动的特点。通过Spring Boot项目整合WebSocket,展示了如何配置依赖、创建WebSocket处理类和配置类。详细解析了WebSocket的原理,包括ServerEndpointExporter的注册过程和请求处理流程。总结了WebSocket与HTTP请求处理的区别,并提供了进一步学习的资源。
Tomcat中的WebSocket是如何实现的?
|
1月前
|
Web App开发 消息中间件 监控
使用 Java + WebSocket 实现简单实时双人协同 pk 答题:技术干货分享
【10月更文挑战第4天】在现代互联网应用中,实时互动已经成为提升用户体验的重要一环。特别是在在线教育、游戏竞技等领域,实时协同功能显得尤为重要。今天,我们将围绕“使用 Java + WebSocket 实现简单实时双人协同 pk 答题”这一主题,分享相关技术干货,帮助你在工作和学习中更好地理解和应用这一技术。
60 2
|
2月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
42 5
|
1月前
|
网络协议 安全 JavaScript
Web实时通信的学习之旅:WebSocket入门指南及示例演示
Web实时通信的学习之旅:WebSocket入门指南及示例演示
125 0
|
2月前
|
Java Linux Python
Linux环境下 代码java调用python出错
Linux环境下 代码java调用python出错
53 3
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
1月前
|
Java 大数据 开发工具
java学习——环境准备(1)
java学习——环境准备(1)
42 0