程序化 ServerEndpoint 在嵌入式 Tomcat 中不起作用

Posted

技术标签:

【中文标题】程序化 ServerEndpoint 在嵌入式 Tomcat 中不起作用【英文标题】:Programmatic ServerEndpoint wont work within Embedded Tomcat 【发布时间】:2018-11-26 05:13:20 【问题描述】:

我有一个嵌入式 tomcat 正在运行,我正在尝试以编程方式设置 websocket,但不能使用注释,因为我动态获取上下文路径列表。使用 Java 8 和 Tomcat 7。

下面是我正在使用的代码,

嵌入式Tomcat,

public class EmbeddedTomcat 
/**
 * @param args
 * @throws LifecycleException 
 */
public static void main(String[] args) throws LifecycleException 
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(5555);

    Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());

    Tomcat.addServlet(ctx, "hello", new HttpServlet() 
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) 
                throws ServletException, IOException 
            Writer w = resp.getWriter();
            w.write("Hello World !!");
            w.flush();
            w.close();
        
    );

    ctx.addServletMapping("/hello", "hello");

    ctx.addApplicationListener(WSContextListener.class.getName());

    tomcat.start();
    tomcat.getServer().await();

WebSocket 监听器动态添加端点,

public class WSContextListener extends WsContextListener 

@Override
public void contextInitialized(ServletContextEvent sce) 
    super.contextInitialized(sce);

    ServerContainer sc =
            (ServerContainer) sce.getServletContext().getAttribute(
                    Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
    try 
        ServerEndpointConfig endPointConfig = ServerEndpointConfig.Builder
                .create(WSEndpoint.class, "/wshello")
                .build();
        sc.addEndpoint(endPointConfig);
     catch(DeploymentException de) 
        de.printStackTrace();
    

实际的服务器端点类,

public class WSEndpoint extends Endpoint 

/* (non-Javadoc)
 * @see javax.websocket.Endpoint#onOpen(javax.websocket.Session, javax.websocket.EndpointConfig)
 */
@Override
public void onOpen(Session session, EndpointConfig endpointConfig) 
    System.out.println(String.format("Opened a new session with Id[%s] associated to endpoint[%s]", session.getId(), session.getRequestURI().getPath()));

    session.addMessageHandler(new MessageHandler.Whole<String>() 
        @Override
        public void onMessage(String data) 
            System.out.println("Data received - " + data);
            session.getAsyncRemote().sendText(data);
        
    );


@Override
public void onClose(Session session, CloseReason closeReason) 
    System.out.println(String.format("Closing the connection to endpoint[%s] for session Id[%s] ", session.getRequestURI().getPath(), session.getId()));
    super.onClose(session, closeReason);


@Override
public void onError(Session session, Throwable throwable) 
    System.out.println(String.format("Error [%s] occurred on session Id[%s] associated to endpoint[%s]", throwable.getMessage(), session.getId(), session.getRequestURI().getPath()));
    super.onError(session, throwable);

最后,连接到 Websocket 的 javascript 位,

var webSocket = new WebSocket("ws://localhost:5555/wshello");

我可以访问 servlet (http://localhost:5555/hello) 并且该位有效。当我尝试通过上面的 javascript 代码访问 websocket 时,它失败了,

websocket_test.html:18 WebSocket 连接到“ws://localhost:5555/wshello”失败:WebSocket 握手期间出错:意外响应代码:404

【问题讨论】:

【参考方案1】:

试试这个

var webSocket = new WebSocket("ws://localhost:5555/hello", "protocol");

添加到客户端处理 websocket 的协议(可选)。 删除 wshello "ws"

【讨论】:

javascript api 不是问题,因为如果我尝试访问公共 websocket 服务器,它可以工作。 var webSocket = new WebSocket("wss://echo.websocket.org") 问题似乎出在服务器端。此外,为 websocket 配置的端点是“wshello”,所以不知道为什么应该在那里使用 servlet 端点“hello”。

以上是关于程序化 ServerEndpoint 在嵌入式 Tomcat 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

传统@ServerEndpoint方式开发WebSocket应用和SpringBoot构建WebSocket应用程序

传统@ServerEndpoint方式开发WebSocket应用和SpringBoot构建WebSocket应用程序

@ServerEndpoint 和 Spring MVC 注入

从 Web Socket @ServerEndpoint 中的 HttpServletRequest 访问 HttpSession

ServerEndpoint 和 web.xml

@ServerEndpoint 和 @Autowired