spring boot整合tomcat、jetty中的websocket

前言

tomcat和jetty都遵循JSR 356的注解规范. 如果是采用interface继承的方式, jetty的websocket无法使用.

但是jetty自己实现了一套新的注解和接口.
JSR 356的规范有个缺陷, 没有获取IP的接口, 需要特殊实现
示例代码
https://github.com/wzjwhut/websocket

JSR 356注解方式实现

@ServerEndpoint声明

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import java.io.IOException;

@Component
@Log4j2
@ServerEndpoint(value = "/echo1",
        configurator=WebSocketAnnotationDemo.Configurator.class,
        subprotocols={"device", "user"})
public class WebSocketAnnotationDemo {
	//每当有一个新的连接时, 都会创建新的WebSocketAnnotationDemo 

    public static  class Configurator extends ServerEndpointConfig.Configurator{
        @Override
        public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request,
                                    HandshakeResponse response) {
            log.info("modifyHandshake: {}", request.getRequestURI());
            HttpSession httpSession = (HttpSession) request.getHttpSession();
            String ip = (String) httpSession.getAttribute("getRemoteAddr");
            if(ip != null) {
                config.getUserProperties().put("getRemoteAddr", ip);
            }
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        log.info("onMessage: {}", message);
    }

    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        log.info("onOpen: {}, this: {}, {}", session.getClass(), this.hashCode(), session.getNegotiatedSubprotocol());
        log.info("uri: {}", session.getRequestURI().getPath());
    }

    @OnClose
    public void onClose(Session session) {
        log.info("onClose: {}", session);
    }

    @OnError
    public void onError(Throwable ex){
        log.info("onError: ", ex);
    }
}

通过WebListener来读取IP

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Log4j2
@Component  //使用spring时, 这个是必需的
@WebListener
public class MyServletListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
    }
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        log.info("requestInitialized");
        HttpServletRequest request=(HttpServletRequest) sre.getServletRequest();
        HttpSession session=request.getSession();
        log.info("client ip: {}", sre.getServletRequest().getRemoteAddr());
        session.setAttribute("getRemoteAddr", sre.getServletRequest().getRemoteAddr());
    }
}

Client测试

import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.io.IOException;
import java.net.URI;

@Configuration
@Log4j2
public class ClientTest {

    @ClientEndpoint
    public static class Client{
        @OnMessage
        public void onMessage(String message, Session session) throws IOException {
            log.info("[client] onMessage: {}", message);
        }

        @OnOpen
        public void onOpen(Session session) throws IOException{
            log.info("onOpen: {}", session);
            session.getBasicRemote().sendText("hello");
        }

        @OnClose
        public void onClose(Session session) {
            log.info("onClose: {}", session);
        }

        @OnError
        public void onError(Throwable ex){
            log.info("onError: ", ex);
        }
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            try {
                log.info("websocket-spring run");
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                container.connectToServer(new Client(), new URI("ws://127.0.0.1/echo1"));
            }catch(Throwable ex){

            }

            try {
                log.info("websocket-spring run");
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                container.connectToServer(new Client(), new URI("ws://127.0.0.1/echo2"));
            }catch(Throwable ex){

            }
        };
    }
}

spring boot启动和测试

@ComponentScan //必需要
@EnableAutoConfiguration
@Log4j2
public class Application  extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication a = new SpringApplication(Application.class);
        a.run(Application.class, args);
    }
    @Override
    protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    @Bean 
    public ServerEndpointExporter serverEndpointExporter() {
    	//必需
        return new ServerEndpointExporter();
    }
}

整合jetty私有的websocket

只介绍注解形式

声明servlet

//spring boot的embeded模式不支持这个注解
//@Component
//@WebServlet(name = "MyEcho WebSocket Servlet", urlPatterns = { "/echo3" })
public class JettyWebsocketServlet extends WebSocketServlet {

    @Override
    public void configure(WebSocketServletFactory webSocketServletFactory) {
        webSocketServletFactory.getPolicy().setIdleTimeout(10000);
        webSocketServletFactory.register(JettyWebsocketAnnotation.class);
    }
}

声明websocket

@WebSocket //必需
@Log4j2
public class JettyWebsocketAnnotation {
    Session session;

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {

    }

    @OnWebSocketConnect
    public void onConnect(Session session) {
        log.info("Got connect: {}", session.getClass());
        this.session = session;
    }

    @OnWebSocketMessage
    public void onMessage(String msg) {
        log.info("onMessage: {}", msg);
        try {
            session.getRemote().sendString(msg);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

spring boot配置


@ComponentScan
@EnableAutoConfiguration
@EnableWebSocket
@Log4j2
public class Application  extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication a = new SpringApplication(Application.class);
        a.run(Application.class, args);
    }
    @Override
    protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    @ClientEndpoint
    public static class Client{
        @OnMessage
        public void onMessage(String message, Session session) throws IOException {
            log.info("[client] onMessage: {}", message);
        }

        @OnOpen
        public void onOpen(Session session) throws IOException{
            log.info("onOpen: {}", session);
            session.getBasicRemote().sendText("hello");
        }

        @OnClose
        public void onClose(Session session) {
            log.info("onClose: {}", session);
        }

        @OnError
        public void onError(Throwable ex){
            log.info("onError: ", ex);
        }

    }

    @Bean
    public ServletRegistrationBean<HttpServlet> jettyWebsocket1() {
        ServletRegistrationBean<HttpServlet> servRegBean = new ServletRegistrationBean<>();
        servRegBean.setServlet(new JettyWebsocketServlet());
        servRegBean.addUrlMappings("/echo3");
        return servRegBean;
    }
    @Bean
    public ServletRegistrationBean<HttpServlet> jettyWebsocket2() {
        ServletRegistrationBean<HttpServlet> servRegBean = new ServletRegistrationBean<>();
        servRegBean.setServlet(new JettyWebsocketServlet2());
        servRegBean.addUrlMappings("/echo4");
        return servRegBean;
    }


    @Bean
    public ApplicationRunner runner() {
        return args -> {
            log.info("websocket-spring run");
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            container.connectToServer(new Client(), new URI("ws://127.0.0.1:80/echo3"));
        };
    }
}

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/88357845