文章目录
前言
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"));
};
}
}