精华内容
下载资源
问答
  • 最近研究了两种服务器推送技术,一种是**WebSocket**技术,一种是基于**Servlet3.0**实现的服务器异步推送技术。 1. WebSocket(聊天室) WebSocket客户端连接类(javaee-api-7.0.jar) import java.io....

    最近研究了两种服务器消息推送技术,一种是WebSocket技术,一种是基于Servlet3.0实现的服务器异步消息推送技术,具体实现如下:

    1. WebSocket(聊天室)

    WebSocket客户端连接类(javaee-api-7.0.jar)

    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    /**
     * @ServerEndpoint 功能主要是将CharRoomServer 定义为websocket服务器端
     */
    @ServerEndpoint("/ws/charRoomServer")
    public class CharRoomServer {
    
    	// 会话,主要用于向客户端发送数据
    	private Session session;
    	
    	public Session getSession() {
    		return session;
    	}
    
    	/**
    	 * websocket连接
    	 * @param session
    	 */
    	@OnOpen
    	public void onOpen(Session session){
    		this.session = session;
    		SessionManager.add(this);     
    	}
    	
    	/**
    	 * websocket断开
    	 */
    	@OnClose
    	public void onClose(){
    		SessionManage.remove(this);
    	}
    
    	/**
    	 * websocket消息
    	 * @param message
    	 * @param session
    	 */
    	@OnMessage
    	public void onMessage(String message, Session session) {
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"%s\",\"content\":\"%s\"}";
    		String msg = String.format(messageFormat, sdf.format(date),
    				"客户端" + session.getId(), message);
    		// 广播
    		SessionManage.broadCast(msg);
    	}
    	
    	/**
    	 * websocket错误
    	 * @param session
    	 * @param error
    	 */
    	@OnError
    	public void onError(Session session, Throwable error){
    		error.printStackTrace();
    	}
    	
    	/**
    	 * 广播消息方法
    	 * @param message
    	 * @throws IOException
    	 */
    	public void sendMessage(String message) throws IOException{
    		this.session.getBasicRemote().sendText(message);
    	}
    
    }
    

    SessionManage类

    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Date;
    
    public class SessionManage {
    
    	private static Collection<CharRoomServer> charRoomServers = Collections.synchronizedCollection(new ArrayList<CharRoomServer>());
    	
    	/**
    	 * 广播
    	 * @param msg
    	 */
    	public static void broadCast(String msg){
    		for (CharRoomServer charRoomServer : charRoomServers) {
    			try {
    				charRoomServer.sendMessage(msg);
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	/**
    	 * 新增客户端服务器
    	 * @param charRoomServer
    	 */
    	public static void add(CharRoomServer charRoomServer){
    		// 使用时间构造登录信息
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"系统\",\"content\":\"%s\"}";
    		String content = "欢迎客户端" + charRoomServer.getSession().getId();
    		String message = String.format(messageFormat, sdf.format(date), content);
    		// 广播
    		broadCast(message);
    		// 加入服务组
    		charRoomServers.add(charRoomServer);
    	}
    	
    	/**
    	 * 客户端服务器退出
    	 * @param charRoomServer
    	 */
    	public static void remove(CharRoomServer charRoomServer){
    		// 客户端服务器退出
    		charRoomServers.remove(charRoomServer);
    		// 使用时间构造退出信息
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"系统\",\"content\":\"%s\"}";
    		String name = "客户端" + charRoomServer.getSession().getId();
    		String message = String.format(messageFormat, sdf.format(date), name + "已退出");
    		// 广播
    		broadCast(message);
    	}
    	
    }
    
    

    2. Servlet3.0(SpringBoot:DeferredResult)

    Application 入口类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableAsync
    @EnableScheduling
    public class Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Application.class, args);
    	}
    
    }
    

    AsyncController异步控制器

    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.async.DeferredResult;
    
    /**
     * 异步控制器
     */
    @RequestMapping("/async")
    @RestController
    public class AsyncController {
        private DeferredResult<String> deferredResult;
    
        @RequestMapping("/getDeferredResult")
        public DeferredResult<String> getDeferredResult() {
            deferredResult = new DeferredResult<String>();
            return deferredResult;
        }
    
        /**
         * 每2秒执行一次
         */
        @Scheduled(fixedDelay = 2000)
        public void refreshDeferredResult() {
            if (deferredResult != null) {
                String result = "getDeferredResult:" + System.currentTimeMillis();
                deferredResult.setResult(result);
            }
        }
    
        @RequestMapping("/getResult")
        public String getResult() {
            return "getResult";
        }
    }
    

    前端页面关键js代码

    function getDeferredResult() {
        $.ajax({
            url:"/async/getDeferredResult",
            type:"post",
            success:function(data){
                $("table").html(data);
                getDeferredResult();
            }
        });
    }
    getDeferredResult();
    

    pom文件配置

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example.async</groupId>
        <artifactId>async</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
        <!-- 编码和版本 -->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <maven.compiler.plugin.version>3.7.0</maven.compiler.plugin.version>
        </properties>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    </project>
    
    展开全文
  • websocket+servlet简单案例代码
  • websocket访问ServletAPI

    千次阅读 2014-08-25 09:58:30
    websocket实用注解的方式来完成服务端配置,使用的协议也是WebSocket,这之前的基于Ajax方式的轮询长连接方式都不相同,所以访问ServletAPI方式也有所不同,那我们怎么才能让websocket访问到ServletAPI呢?...

    对于一个java web程序来说或多或少都要使用ServletAPI,websocket也不会例外。但是

    websocket实用注解的方式来完成服务端配置,使用的协议也是WebSocket,这和之前的基于Ajax方式的轮询和长连接方式都不相同,所以访问ServletAPI方式也有所不同,那我们怎么才能让websocket访问到ServletAPI呢?

    WebSocket有一个Configurator(该类的完整路劲javax.websocket.server.ServerEndpointConfig.Configurator)类,我们可以通过继承这个类,重写其中的方法(modifyHandshake),然后通过注解的方式配置,达到我们访问ServletAPI的目的。下面我以websocket获取HttpSession为例演示,具体代码如下:

    继承Configurator类

             publicclass MyConfiguratorextendsConfigurator {

     

            @Override

            publicvoid modifyHandshake(ServerEndpointConfig sec,

                    HandshakeRequest request,HandshakeResponse response) {

                        //获取HttpSession

                        HttpSession httpSession =(HttpSession)request.getHttpSession();

                        //保存HttpSessionUserProperties

                        sec.getUserProperties().put("httpSession", httpSession);

            }

       

    }

     

    配置websocket类,并在该类中获取已经存放的httpSession

    @ServerEndpoint(value="server",configurator=MyConfigurator.class)

    publicclass Server {

        @OnOpen

        publicvoid start(Session session){

            //注意两种Session的区别,参数中Sessionwebsocketsession,用这个//session可以获取UserProperties,再通过该对象获取已经存入其中的HttpSession对象

            HttpSession httpSession =(HttpSession)session.getUserProperties().get("httpSession");

            //然后至于你怎么使用该httpSession对象那就要看你的目的了。

        }

    }

     

    展开全文
  • 我们的目标是以高容量,低延迟的方式支持HTTP,HTTP / 2和WebSocket之类的Web协议,以提供最佳性能,同时保持易用性以及与多年Servlet开发的兼容性。 Jetty是一种现代的完全异步的Web服务器,作为一种面向组件的...
  • webSocket 案例 原生使用servlet

    千次阅读 2016-04-01 16:32:19
    原生使用servlet创建WebSocket案例 注意问题:  1、在网上的案例中 一般会引入两个jar包 catalina.jar,websocket-api.jar 两个jar包但是在项目启动时会出现报错信息所以我做的如下案例将catalina.jar去掉了  2...
    原生使用servlet创建WebSocket案例 
    
    注意问题:
                    1、在网上的案例中 一般会引入两个jar包  catalina.jar, websocket-api.jar 两个jar包但是在项目启动时会出现报错信息所以我做的如下案例将catalina.jar去掉了
                    2、 catalina.jar, websocket-api.jar 其实这两个jar包在tomcat7.0.68中是存在的 所以在运行项目于时一定要引入tomcat中的jar文件否则项目就会报错,原因就是这两个jar对tomcat中其他的jar有依赖关        系,在项目启动时 就会找不到相关的jar
                    
    所以推测一下 在网上有一种说法是 在使用tomcat时要查看版本说是tomcat7.0以上 但是如果是以上的方法 可以尝试一下 不一定适用tomcat7.0以上 导入相关的jar包 应该也能实现 我就没有尝试 太懒了 ~
    其实  catalina.jar, websocket-api.jar 直接引入tomcat中也能使用 上面的就是记录一下 项目中遇到的问题 。留着以后发现问题。
    servlet端
    package com.webscoletdemo.action;
    import  java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import javax.xml.ws.WebServiceFeature;
    import org.apache.catalina.websocket.MessageInbound;
    import org.apache.catalina.websocket.WebSocketServlet;
    import org.apache.catalina.websocket.WsOutbound;

    public class WebScoketAction extends WebSocketServlet {  
        private static final long serialVersionUID = -4853540828121130946L;  
        private static List<MyMessageInbound> mmiList = new ArrayList<MyMessageInbound>();  

        @Override  
        protected MyMessageInbound createWebSocketInbound(String str,  
                HttpServletRequest request) {  
            request.getParameter("");       
            return new MyMessageInbound();  
        }  

        private class MyMessageInbound extends MessageInbound {  
            WsOutbound myoutbound;  
            //开启websocket
            public void onOpen(WsOutbound outbound) {  
                try {  
                    System.out.println("Open Client.");  
                    this.myoutbound = outbound;  
                    mmiList.add(this);  
                    outbound.writeTextMessage(CharBuffer.wrap("Hello!"));  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  

            //关闭websocket
            public void onClose(int status) {  
                System.out.println("Close Client.");  
                mmiList.remove(this);  
            }  
           //当服务器端收到信息的时候,就对所有的连接进行遍历并且把收到的信息发送给所有用户  
            public void onTextMessage(CharBuffer cb) throws IOException {  
                System.out.println("Accept Message : " + cb);  
                for (MyMessageInbound mmib : mmiList) {  
                    CharBuffer buffer = CharBuffer.wrap(cb);  
                    mmib.myoutbound.writeTextMessage(buffer);  
                    mmib.myoutbound.flush();  
                }  
            }  


            public void onBinaryMessage(ByteBuffer bb) throws IOException {  

            }  
        }  
    }  
    index.jsp jsp端
    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">

        <title>My JSP 'index.jsp' starting page</title>
        <meta http-equiv="pragma" content="no-cache">
        <meta http-equiv="cache-control" content="no-cache">
        <meta http-equiv="expires" content="0">    
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="This is my page">
        <!--
        <link rel="stylesheet" type="text/css" href="styles.css">
        -->
        <script type="text/javascript">
    var socket = new WebSocket("ws://localhost:8080/WebSocketDemo/aa.do?aaa=11");
    socket.onopen = function(){
        //浏览器socket开始进入
        console.log("open");
        //向服务器端发送信息
        socket.send("open+hello");
    };
    socket.onmessage = function(evt)
    {
        console.log("message");
        console.log("onmessage:"+evt.data);
    };
    socket.onclose = function(evt){
        console.log("close");
        console.log("onclose:"+evt.data);
    };
    socket.onerror = function(evt)
    {
        console.log("error");
        console.log("onerror:"+evt.data);
    };
    function send()
    {
        //socket.send("sendMessage");
    }
    function closeWebSocket(){
         socket.close();
    }

    </script>
      </head>

      <body>
        This is my JSP page. <br>
         Welcome<br/>
       <button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
        <div id="message">
        </div>
      </body>
    </html>


    web.xml 端
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" 
        xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee 
      <display-name></display-name>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      <servlet>
        <servlet-name>aa</servlet-name>
        <servlet-class>com.webscoletdemo.action.WebScoketAction</servlet-class>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>aa</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    </web-app>

    lib:  websocket-api.jar
    展开全文
  • 如果不是很清楚WebSocket协议,可以参考这篇博客。 包结构 Servlet 3.1以上的版本制定了WebSocket的编程规范,位于包...javax.servlet.websocket.server下包含了创建配置WebSocket服务端所需的注解、接口类。 M...

    如果不是很清楚WebSocket协议,可以参考这篇博客

    包结构

    Servlet 3.1以上的版本制定了WebSocket的编程规范,位于包javax.servlet中:
    在这里插入图片描述
    javax.servlet.websocket下包含了客户端和服务器端公用的注解、接口、类和异常
    javax.servlet.websocket.server下包含了创建和配置WebSocket服务端所需的注解、接口和类。
    Maven依赖:

    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.1</version>
        <scope>provided</scope>
    </dependency>
    

    关于WebSocket的简易Demo网上已经有很多了,这里就不介绍这些了。


    Endpoint

    WebSocket是一个建立在TCP基础上的双向通信应用层协议,Endpoint的概念类似于一个Servlet实现类,用于实现WebSocket相关业务逻辑,区别在于Servlet是处理HTTP请求,而EndPoint是处理WebSocket数据帧。例如javax.servlet.websocket.RemoteEndpoint实例代表客户端。

    在实际编程中,Endpoint的编写有两种方式:

    • 基于注解(常用)
      Servlet提供了4个用于修饰方法的注解:@OnOpen@OnMessage@OnError@OnClose,其修饰的方法分别用于在连接建立时回调、收到数据帧时回调、处理逻辑发生异常时回调、连接关闭时回调。例如:

      @ServerEndpoint("/ws/chart")
      public class ChartEndpointImpl {
      	@OnOpen
      	public void open(Session session, EndpointConfig conf) { }
      	@OnMessage
      	public void message(Session session, String message) { }
      	@OnError
      	public void error(Session session, Throwable error) { }
      	@OnClose
      	public void close(Session session, CloseReason reason) { }
      }
      

      @ServletEndpoint需要指定一个URI,在上述例子中,当客户端向/ws/chart发起WebSocket握手HTTP请求时,默认情况下会创建一个新的ChartEndpointImpl实例,并调用@OnOpen修饰的方法(如果存在的话)。
      此外,各个方法的参数列表并不是固定的,具体规则如下:

      • 用户可以在任何方法中的参数列表指定一个Session类型的参数。
      • @OnMessage修饰的方法中,可以传入消息对象(类型由Decoder决定,我们稍作讨论),一个ServerEndpoint可以具有多个@OnMessage修饰的方法,前提是它们的参数列表互不相同,并且有对应的Docoder
      • @OnError方法中,可以传入异常Throwable类型的参数。
      • @OnClose方法中,可以传入CloseReason类型的参数,以分析WebSocket连接关闭的具体原因。
    • 基于javax.servlet.websocket.Endpoint抽象类(较少使用)
      我们可以通过继承EndPoint方式来重写控制WebSocket连接生命周期相关的回调方法,例如我们实现一个EndpointImpl

      public class EndpointImpl extends Endpoint {
      	@Override
          public void onOpen(Session session, EndpointConfig config) {
          	//连接建立时回调
          }
      
          public void onClose(Session session, CloseReason closeReason) {
              // 连接被关闭时回调
          }
      
          public void onError(Session session, Throwable throwable) {
              // 连接发生异常时回调
          }
      }
      

      虽然Endpoint没有定义onMessage处理方法,但是我们可以通过在onOpen方法通过Session对象添加MessageHandler对象来实现onMessage相关的逻辑。

    @ServerEndpoint注解除了可以指定URI以外,还可以定义以下内容:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface ServerEndpoint {
    	String value();  //对应的URI
    	String[] subprotocols() default {};  //对应的子协议,由握手请求头Sec-WebSocket-Protocol指定
    	Class<? extends Decoder>[] decoders() default {};  //解码器
    	Class<? extends Encoder>[] encoders() default {};  //编码器
    	public Class<? extends ServerEndpointConfig.Configurator> configurator()  //ServerEndpointConfig实现类
    	            default ServerEndpointConfig.Configurator.class;
    }
    

    value就是我们刚才提到的URI,除了固定的URI,还可以将URI一部分作为参数,例如:

    @ServerEndpoint("/chart/{id}")
    public class EndpointImpl {
    	@OnOpen
    	public void example(Session session, EndpointConfig cfg, @PathParam("id") String id) { }
    }
    

    subprotocols用于实现WebSocket协议扩展,当有多个@ServerEndpoint修饰的类的URI相同时,就根据WebSocket握手阶段客户端发送的HTTP请求头中Sec-WebSocket-Protocol(默认情况下没有该请求头)来选取对应的ServerEndpoint
    EncoderDecoder对应编码器和解码器,如果@OnMessage修饰的方法的参数列表有非基本类型的对象,就需要解码器将二进制流或者字符流转换为Java对象。EncoderDecoder在Servlet规范中只是两个接口,其实现类需要自己根据业务需求编写。


    Session

    WebSocket不像HTTP,它是一个有状态的协议,WebSocket在连接建立时创建Session对象,在连接关闭时删除本次连接对应的Session对象,其生命周期等于WebSocket连接。而HTTP协议对应HttpSession通过HTTP协议传来的会话ID来识别,当会话过期后才会删除其HttpSession对象,其生命周期与过期时间有关。

    Session本身是一个接口,并继承了java.io.Closeable接口,定义了一些与会话相关的方法,其具体实现由容器决定,它包含几个类型的方法:

    • 获取WebSocket容器:

      WebSocketContainer getContainer();
      
    • 获取客户端握手请求相关的信息:

      // 获取WebSocket协议版本,对应客户端握手请求头的Sec-WebSocket-Version字段,一般为13
      String getProtocolVersion();
      // 获取握手请求头的Sec-WebSocket-Protocol对应的值
      String getNegotiatedSubprotocol();
      // 获取请求URI,在上述例子中就是/ws/chart
      URI getRequestURI();
      // 获取请求参数
      Map<String, List<String>> getRequestParameterMap();
      // 同样是获取请求参数,如果参数存在相同的键,则一般是取第一个
      Map<String,String> getPathParameters();
      // 参数原字符串,例如请求/ws/chart?id=123&sid=123,那么该方法会返回"id=123&sid=123"
      String getQueryString();
      //获取Sec-WebSocket-Extensions请求头中的所有字段
      List<Extension> getNegotiatedExtensions();
      
    • 获取、修改WebSocket连接本身属性相关的信息:

      // 当前WebSocket连接是否采用了SSL,也就是判定是ws://还是wss://
      boolean isSecure();
      // 当前WebSocket连接是否活跃(已经打开)
      boolean isOpen();
      // 最大空闲时间,当当前时间减去最近一次交互数据的时间大于该值时,连接会被关闭
      long getMaxIdleTimeout();
      // 设置最大空闲时间
      void setMaxIdleTimeout(long timeout);
      // 设置存储二进制类型(opcode为0x2)的消息缓冲区最大字节数
      void setMaxBinaryMessageBufferSize(int max);
      // 获取存储二进制类型(opcode为0x2)的消息缓冲区最大字节数
      int getMaxBinaryMessageBufferSize();
      // 设置存储文本类型(opcode为0x1)的消息缓冲区最大字符数
      void setMaxTextMessageBufferSize(int max);
      // 获取存储文本类型(opcode为0x1)的消息缓冲区最大字符数
      int getMaxTextMessageBufferSize();
      // Session在服务器内部的唯一ID,由容器设置,对客户端不可见。
      String getId();
      // 关闭WebSocket连接
      @Override void close() throws IOException;
      // 关闭WebSocket连接,并设置关闭原因
      void close(CloseReason closeReason) throws IOException;
      
    • 获取、修改本次会话的MessageHandler

      // 添加MessageHandler
      void addMessageHandler(MessageHandler handler) throws IllegalStateException;
      // 获取所有的MessageHandler
      Set<MessageHandler> getMessageHandlers();
      // 删除MessageHandler
      void removeMessageHandler(MessageHandler listener);
      //添加Partial类型的MessageHandler
      <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler) throws IllegalStateException;
      //添加Whole类型的MessageHandler
      <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler) throws IllegalStateException;
      

      @OnMessage注解修饰的方法其实可以看成是一个特殊的MessageHandler

    • 获取同步、异步模式的RemoteEndpoint

      //获取异步RemoteEndpoint
      RemoteEndpoint.Async getAsyncRemote();
      //获取同步RemoteEndpoint
      RemoteEndpoint.Basic getBasicRemote();
      

      我们可以利用RemoteEndpoint对象随时向客户端发送WebSocket数据帧。

    • 其它:

      //获取用户参数,初始参数等价于EndpointConfig.getUserProperties, 可以存放一些当前会话的临时参数,类似Servlet的setAttribute
      Map<String, Object> getUserProperties();
      //获取用户权限相关对象
      Principal getUserPrincipal();
      //当前URI所对应的Endpoints中所有活跃Session的对象,可使用该对象进行诸如消息的广播之类的功能
      Set<Session> getOpenSessions();
      

    消息的发送

    在前面介绍Session的API时候提到过,消息的发送分为异步发送和同步发送,对应于Session对象的getAsyncRemotegetBasicRemote方法所返回的RemoteEndpoint.Async对象和RemoteEndpoint.Basic对象,RemoteEndpoint.AsyncRemoteEndpoint.Basic本身是一个接口,都继承于RemoteEndpoint

    public interface RemoteEndpoint {
    	//设置是否允许暂存消息
    	void setBatchingAllowed(boolean batchingAllowed) throws IOException;
    	//是否允许暂存消息
    	boolean getBatchingAllowed();
    	//刷新所有缓冲区中的消息到客户端
    	void flushBatch() throws IOException;
    	//发送ping消息
    	void sendPing(ByteBuffer applicationData) throws IOException, IllegalArgumentException;
    	//发送pong消息
    	void sendPong(ByteBuffer applicationData) throws IOException, IllegalArgumentException;
    }
    

    RemoteEndpoint定义了一些基本的方法:发送PING数据帧(opcode0x9)和发送PONG数据帧(opcode0xA),并可以刷新暂存的数据帧,定义是否允许暂存数据帧的相关方法。

    RemoteEndpoint.Basic接口:

    interface Basic extends RemoteEndpoint {
    	//发送文本类型(opcode为0x1)的消息
    	void sendText(String text) throws IOException;
    	//发送二进制类型(opcode为0x2)的消息
    	void sendBinary(ByteBuffer data) throws IOException;
    	//发送文本消息,并可以标记是否是最后一条消息,方便客户端拼接数据帧,isLast为true时FIN会被标记为1
    	void sendText(String fragment, boolean isLast) throws IOException;
    	//发送二进制消息,并可以标记是否是最后一条消息,方便客户端拼接数据帧,isLast为true时FIN会被标记为1
    	void sendBinary(ByteBuffer partialByte, boolean isLast) throws IOException;
    	//获取面向客户端的字节输出流,可调用其write方法直接写入数据
        OutputStream getSendStream() throws IOException;
        //获取面向客户端的字符输出流,可调用其write方法直接写入字符串
        Writer getSendWriter() throws IOException;
        //直接发送对象,必须要有对应的Encoder,否则会抛出EncodeException
        void sendObject(Object data) throws IOException, EncodeException;
    }
    

    RemoteEndpoint.Basic接口定义了同步发送字符类型的数据帧和二进制类型的数据帧的方法,也可以直接发送对象(在有对应的Encoder的前提下)。这些方法在被调用时,会一直阻塞到数据发送完成才会返回,对性能有一定的影响。

    RemoteEndpoint.Async接口:

    interface Async extends RemoteEndpoint {
    	//发送消息的超时时间,若在规定时间内消息还没发送完成,就放弃发送
    	long getSendTimeout();
    	//设置超时时间
    	void setSendTimeout(long timeout);
    	//发送文本类型数据帧,立刻返回,在发送完成/超时/异常后会回调SendHandler
        void sendText(String text, SendHandler completion);
        //发送文本类型数据帧,立刻返回Future方便日后查询发送结果
        Future<Void> sendText(String text);
        //发送二进制数据帧,立刻返回Future方便日后查询发送结果
        Future<Void> sendBinary(ByteBuffer data);
        //发送二进制类型数据帧,立刻返回,在发送完成/超时/异常后会回调SendHandler
        void sendBinary(ByteBuffer data, SendHandler completion);
        //发送对象,立刻返回,需要有对应的Encoder
        Future<Void> sendObject(Object obj);
        //发送对象,立刻返回,在发送完成/超时/异常后会回调SendHandler
        void sendObject(Object obj, SendHandler completion);
    }
    

    相比RemoteEndpoint.Basic相关方法,RemoteEndpoint.Async就是可以异步发送数据,方法在调用后会立刻返回,由容器中的相关线程处理。用户事后可以通过Future查询执行结果,也可以通过指定SendHandler回调:

    public interface SendHandler {
        void onResult(SendResult result);
    }
    

    SendResult对象仅包含两个成员变量:

    public final class SendResult {
        private final Throwable exception;
        private final boolean ok;
        //...
    }
    

    如果执行成功,那么oktrue并且exceptionnull。如果执行失败,那么okfalse并且exception不为null


    数据帧处理流

    WebSocket数据帧的接收和发送可以用以下图来概括:
    在这里插入图片描述
    当Web容器收到一个数据帧时,因为在握手阶段已经确定Endpoint,所以只需要根据WebSocket数据帧的opcode字段判断是二进制类型的数据还是字符类型数据,前者使用Decoder.Binary或者Decoder.BinaryStream实现类,后者使用Decoder.Text或者Decoder.TextStream实现类。解析成Java对象后,只需要在@OnMessage修饰的方法中找出合适的即可(符合参数列表类型的)。

    Decoder接口定义如下:

    public interface Decoder {
        void init(EndpointConfig endpointConfig);  //初始化实例
        void destroy();  //销毁实例
        interface Binary<T> extends Decoder {
            T decode(ByteBuffer bytes) throws DecodeException;
            boolean willDecode(ByteBuffer bytes);
        }
    
        interface BinaryStream<T> extends Decoder {
            T decode(InputStream is) throws DecodeException, IOException;
        }
    
        interface Text<T> extends Decoder {
            T decode(String s) throws DecodeException;
            boolean willDecode(String s);
        }
    
        interface TextStream<T> extends Decoder {
            T decode(Reader reader) throws DecodeException, IOException;
        }
    }
    

    可以看出Decoder是多例的,其生命周期等同于一个WebSocket连接,在完成WebSocket握手后,Decoder会被实例化并调用其init方法(需要保证Decoder有一个无参的公有构造方法),在连接失效后,会调用destory方法,该方法一般用于释放资源等。

    Decoder分为两大类:二进制WebSocket数据帧解码器和字符类型WebSocket数据帧解码器。
    其中,如果存在BinaryStreamTextStream,那么会直接调用该方法的decode方法解析字节流或者字符流。如果存在TextBinary类型的解码器,则会首先调用willDecode方法,如果返回true,那么才会调用decode方法,否则会尝试寻找另外一个Decoder实现类。

    数据发送需要经过编码器Encoder,生命周期和Decoder相同。只有在调用RemoteEndpointsendObject时才会利用到Encoder,其它方法都是直接传递给客户端的。Encoder同样区分字符类型和二进制类型:

    public interface Encoder {
        void init(EndpointConfig endpointConfig);
        void destroy();
        interface Text<T> extends Encoder {
            String encode(T object) throws EncodeException;
        }
        interface TextStream<T> extends Encoder {
            void encode(T object, Writer writer) throws EncodeException, IOException;
        }
        interface Binary<T> extends Encoder {
            ByteBuffer encode(T object) throws EncodeException;
        }
        interface BinaryStream<T> extends Encoder {
            void encode(T object, OutputStream os) throws EncodeException, IOException;
        }
    }
    

    如果一个消息是由多个对象组成的,那么实现带Stream的Encoder,否则选择不带Stream的Encoder
    带Stream的encode方法需要根据传入的object对象写入到OutputStream中,类似于:

    void encode(T object, OutputStream os) throws EncodeException, IOException {
    	byte[] b = object.serialize();
    	os.write(b);
    }
    

    不带Stream的encode方法一般直接返回其解码结果就行:

    ByteBuffer encode(T object) throws EncodeException {
    	byte[] b = object.serialize();
    	return ByteBuffer.wrap(b);
    }
    

    本文由官方文档以及源代码整理而成,如果有错误欢迎在评论区指出。

    展开全文
  • tomcat8.5 jsp servlet WebSocket等api文档涵盖了整个 Javaweb开发中所需文档内容。
  • websocket实例

    2016-08-19 11:01:17
    websocket push实例,websocket server,websocket client,websocket push servlet
  • 第一部分使用原生servlet+jsp方式开发websocket应用,旨在还原最基本的websocket实现过程,力求从原生代码的角度剖析websocket技术架构的本质特征; websocket应用开发视频01-servlet+jsp视频链接:...
  • Java WebSocket基于注解配置访问ServletAPI配置类package com.xyes.music.websocket;import com.sun.xml.internal.bind.v2.runtime.reflect.opt.Const; import com.xyes.music.context.Consts;import javax.servlet....
  • http://tomcat.apache.org/whichversion.html  
  • 添加依赖 <!-- websocket --> <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> ...
  • 最近在考虑公司主要基础三方库版本统一升级的问题,特看了下tomcat jdk servlet websocket版本的对应关系,如下:  
  • socket servlet websocket 概念 区别与联系

    千次阅读 2016-11-17 15:10:30
    WebSocket API,浏览器服务器只需要做一个握手的动作,然后,浏览器服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处: 1...
  • Servlet实现WebSocket的简单聊天室(二)

    千次阅读 2020-04-26 17:06:28
    >勿以恶小而为之,勿以善小而不为--------------------------刘备 > >劝诸君,多行善事积福报,莫作恶 主要内容: Servlet实现 WebSocket
  • 大三在校学生,学了点websocket的内容,当作笔记; 实现了群发,单独通信也自然会了,就是session.getBasicRemote().sendText(msg);这个语句 群发:就是把当前连接的人的session for循环发送 单独:就是接受发送...
  • Websocket

    2021-08-26 10:04:53
    SpringCloud ZUUL集群 + Nginx + Redis 实现Websocket向客户端推送消息简介Nginx配置Zuul websocket配置Redis配置及websocket配置前端代码 简介 本文主要是针对分布式场景下的使用websocket的一个解决方案。很遗憾的...
  • websocket

    2017-06-15 14:25:37
    spring websocket
  • this.webSocket = new egret.WebSocket(); this.webSocket.addEventListener(egret.ProgressEvent.SOCKET_DATA, onReceiveMessage, this); this.webSocket.addEventListener(egret.Event.CONNECT, onSocketOpen, ...
  • webSocket

    2019-12-12 15:25:47
    webSocket 1_首先创建动态Web项目 2_导入jar 3_创建BitCoinServer类 package com.kemeng.bitcoin; import java.io.IOException; import javax.websocket.OnClose; import javax.websocket.OnError; import ...
  • spring4.0以后加入了对websocket技术的支持 [url]http://blog.csdn.net/gisredevelopment/article/details/38392629[/url] 使用 HTML5 WebSocket 构建实时 Web 应用 [url]...
  • javax.servlet.ServletException: Servlet execution threw an exception org.apache.tomcat.websocket.se 查看一下你的jar文件的目录是否正确,应将jar文件放入WEB-INF的lib目录下,其余的目录都会报错 ...
  • Ka-Websocket Ka Websocket 是一个注释驱动的框架,它允许开发人员以... 目标是在引入 websocket 支持后,为所有主要的 Servlet 容器/应用服务器提供支持。 还支持所有主要的依赖注入框架,如 Spring、Guice CDI。
  • WebSocket

    2019-02-23 12:49:36
    目录 概述 WebSocket 是什么? 为什么需要 WebSocketWebSocket 如何工作?...WebSocket 客户端 ...HTTP WebSocket 有什么关系? Html HTTP 有什么关系? 完整示例 资料 WebSocket 是什...
  • Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员...Websocket实时通信 一、注解配置  Servlet3.0新规范顺应了时代的潮流,使用注解配置,取代混乱的web.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,065
精华内容 8,826
关键字:

websocket和servlet