设为首页 收藏本站
查看: 1923|回复: 0

[经验分享] 在Java7与Tomcat8环境下使用WebSocket实现聊天的示例

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-12-2 09:07:42 | 显示全部楼层 |阅读模式
文中案例在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

上图解释:
  • 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.服务器端实现



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 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的依赖。
1
2
3
4
5
6

            org.java-websocket
            Java-WebSocket
            1.3.0
            runtime
        





  如果开发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实现的聊天示例就结束了。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-34255-1-1.html 上篇帖子: 更改Tomcat startup.bat启动窗口名称 下篇帖子: Android 启动Tomcat服务报错,端口占用解决方案
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表