websocket学习

  • 简介:

    • WebSocket协议在2008年诞生,2011年成为国际标准。

    • WebSocket是一种在单个TCP连接上进行全双工通信的协议,位于 OSI 模型的应用层。

    • WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。

    • 在WebSocket应用程序中,服务器发布WebSocket端点,客户端使用url连接到服务器。建立连接后,服务器和客户端就可以互相发送消息。客户端通常连接到一台服务器,服务器接受多个客户端的连接。

    WebSocket协议:

             WebSocket协议有两个部分:握手和传输。客户端通过向服务端URL发送握手请求来建立连接。握手与现有的基于HTTP的基础结构相兼容。Web服务器将其解释为升级版的HTTP连接请求。

            一个客户端建立连接的握手请求:

1

2

3

4

5

6

7

GET /path/to/websocket/endpoint HTTP/1.1

Host: localhost

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==

Origin: http://localhost

Sec-WebSocket-Version: 13

        一个服务端响应:

1

2

3

4

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

        从上面的请求和响应中可以看出来,一个WebSocket连接的建立,需要客户端和服务端维护一个Key来作为该连接的连接凭证。
        客户端向服务端发送WebSocketKey,服务器根据WebSocketKey生成WebSocketAccept返回给客户端,客户端对WebSocketKey的值再进行相同的操作,如果与服务器返回的Accept的值相匹配,就表示握手成功。握手之后客户端与服务端就互相发送消息。

WebSocket支持的消息类型:

        WebSocket支持文本消息(UTF-8编码)和二进制消息。WebSocket的控制消息由Close、Ping、Pong组成。ping和pong也有可能包含应用程序信息。
        webSocket端点具有如下的URI形式表示:

1

2

ws://host:port/path?query

wss://host:port/path?query

        ws代表未加密的连接,默认端口是80
        wss代表加密的连接,默认端口是443
        path:表示服务器内端点的位置
        query:路径参数信息

创建一个WebSocket程序:

        创建和部署WebSocket端点的过程如下:
                1、创建一个端点类
                2、实现端点的生命周期方法
                3、将业务逻辑添加到端点
                4、将端点部署到Web应用程序中

        注意:与Servlet相反,WebSocket端点类会被实例化多次,容器针对与其部署的URI的每个连接都创建一个实例。每个实例都与一个连接相关联。因为在任何时间都只有一个线程执行端点实例的代码,所以有助于保持每个连接的用户状态简化开发。

    1生命周期方法:

    EndPoint类中定义了三个生命周期方法:onOpen、onClose、onError
    使用注解方式创建服务类

1

2

3

4

5

6

7

8

9

@ServerEndpoint("/echo")

public class EchoEndpoint {

   @OnMessage

   public void onMessage(Session session, String msg) {

      try {

         session.getBasicRemote().sendText(msg);

      } catch (IOException e) { ... }

   }

}

    2业务逻辑方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@ServerEndpoint("/receive")

public class ReceiveEndpoint {

   @OnMessage

   public void textMessage(Session session, String msg) {

      System.out.println("Text message: " + msg);

   }

   @OnMessage

   public void binaryMessage(Session session, ByteBuffer msg) {

      System.out.println("Binary message: " + msg.toString());

   }

   @OnMessage

   public void pongMessage(Session session, PongMessage msg) {

      System.out.println("Pong message: " +

                          msg.getApplicationData().toString());

   }

}

注意:一个服务类最多可以有三个OnMessage注解,每个消息类型分别使用一种方法:Text、Binary、Pong

    3维护客户状态:

        有时在程序中我们可能需要在连接中维护一些用户参数,WebSocket也提供了这样的服务

        Session.getUserProperties获取用户参数信息
        如果要存储所有连接的客户端共有的信息,可以使用静态变量,但是需要用户保证对数据的线程安全访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@ServerEndpoint("/delayedecho")

public class DelayedEchoEndpoint {

   @OnOpen

   public void open(Session session) {

      session.getUserProperties().put("previousMsg", " ");

   }

   @OnMessage

   public void message(Session session, String msg) {

      String prev = (String) session.getUserProperties()

                                    .get("previousMsg");

      session.getUserProperties().put("previousMsg", msg);

      try {

         session.getBasicRemote().sendText(prev);

      } catch (IOException e) { ... }

   }

}

    4数据格式的编码与解码:

    由于客户端和服务端交互可能涉及数据格式的转换,所以提供了Decoder和Encoder的方式解决。
    同时由于WebSocket的@Message注解只能有一个用来传输Text信息或Binary信息,所以要进行最常用的Json->entity转换解析就需要该方法
    Encoder

    Encoder.Text 用于文本消息Encoder.Binary 用于二进制消息

    使用方法:

        1、创建编解码类

1

2

3

4

5

6

7

8

9

10

11

public class MessageATextEncoder implements Encoder.Text<MessageA> {

   @Override

   public void init(EndpointConfig ec) { }

   @Override

   public void destroy() { }

   @Override

   public String encode(MessageA msgA) throws EncodeException {

      // Access msgA's properties and convert to JSON text...

      return msgAJsonString;

   }

}

        2、在端点类注解中添加该Encoder

1

2

3

4

5

@ServerEndpoint(

   value = "/myendpoint",

   encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }

)

public class EncEndpoint { ... }

        

        3、这时候就可以发送MessageA和MessageB两种类型的Text数据

1

2

3

4

MessageA msgA = new MessageA(...);

MessageB msgB = new MessageB(...);

session.getBasicRemote.sendObject(msgA);

session.getBasicRemote.sendObject(msgB);

注意:webSocket会自动寻找使用哪种编码器,所以发送数据统一使用sendObject即可

Decoder

    实现Decoder以将WebSocket消息转换为Java对象

    Decoder.Text 用于文本消息

    Decoder.Binary 用于二进制消息

使用方法:

与Encoder类似

注意:与Encoder不同,Decoder最多可以指定一个Binary和一个Text类型的Decoder,如果有两种以上的Java类型作为文本消息进行发送和接收需要进行定义处理。可以使多个消息继承一个公共的消息父类

1、编写Decoder类,对收到消息类型的不同进行不同的解码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public class MessageTextDecoder implements Decoder.Text<Message> {

   @Override

   public void init(EndpointConfig ec) { }

   @Override

   public void destroy() { }

   @Override

   public Message decode(String string) throws DecodeException {

      // Read message...

      if ( /* message is an A message */ )

         return new MessageA(...);

      else if ( /* message is a B message */ )

         return new MessageB(...);

   }

   @Override

   public boolean willDecode(String string) {

      // Determine if the message can be converted into either a

      // MessageA object or a MessageB object...

      return canDecode;

   }

}

2、在端点类中添加decoders={MessageDecoder.class}

1

2

3

4

5

6

@ServerEndpoint(

   value = "/myendpoint",

   encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },

   decoders = { MessageTextDecoder.class }

)

public class EncDecEndpoint { ... }

3、在@OnMessage方法中使用

1

2

3

4

5

6

7

8

@OnMessage

public void message(Session session, Message msg) {

   if (msg instanceof MessageA) {

      // We received a MessageA object...

   } else if (msg instanceof MessageB) {

      // We received a MessageB object...

   }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值