Tomcat和Jetty对WebSocket的支持

jopen 8年前

 

Tomcat:

       J2EE下面用的最多的容器应该就是tomcat了。说到tomcat对WebSocket的支持,不得不先提一下,目前的WebSocket协议已经经过了好几代的演变,不同浏览器对此协议的支持程度也不同,因此,如果作为服务器,最理想的是支持尽可能多的WebSocket协议版本。

tomcat8真正支持jsr-356(包含对websocket的支持), tomcat7支持部分版本的websocket实现不兼容jsr-356。因此,能用tomcat8的话,还是尽量用。

 

代码实现相当简单,以下是一个列子,只需要tomcat8的基本库,不需要其他依赖。

    import java.io.IOException;          import javax.websocket.OnClose;          import javax.websocket.OnMessage;          import javax.websocket.OnOpen;          import javax.websocket.Session;          import javax.websocket.server.ServerEndpoint;                          @ServerEndpoint("/websocket")          public class WebSocketTest {                        @OnMessage              public void onMessage(String message, Session session) throws IOException,                      InterruptedException {                  // Print the client message for testing purposes                  System.out.println("Received: " + message);                  // Send the first message to the client                  session.getBasicRemote().sendText("This is the first server message");                  // Send 3 messages to the client every 5 seconds                  int sentMessages = 0;                  while (sentMessages < 3) {                      Thread.sleep(5000);                      session.getBasicRemote().sendText("This is an intermediate server message. Count: " + sentMessages);                      sentMessages++;                  }                  // Send a final message to the client                  session.getBasicRemote().sendText("This is the last server message");              }                        @OnOpen              public void onOpen() {                  System.out.println("Client connected");              }                        @OnClose              public void onClose() {                  System.out.println("Connection closed");              }          }    


Jetty:

       Jetty和Tomcat一样,也是一个Servlet的容器。如果说不同之处,那么最大的不同应该是Tomcat采用的是BIO处理方式,也就是说一个request会用一个线程去处理,即使是WebSocket这种长连接,也是会独立开一个线程。作为一个普通的Web服务器,tomcat可以轻松应对耗时比较短的Request/Response。但是如果换成是长连接的WebSocket,那麻烦就来了,对于上万用户的聊天和推送,总不能开上万个线程去处理吧。此时,Jetty的性能就体现出来了,Jetty采用的是NIO,一个线程可以处理多个WebSocket的长链接,如果你的需求是大量耗时比较长的request或者大量长连接,那么建议采用Jetty。

 

        Jetty对WebSocket的实现有点绕,Servlet不再是继承原来的HttpServlet,而是继承WebSocketServlet。此处要注意导入jetty-util.jar和jetty-websocket.jar两个包,否则可能会有class not found错误。

ReverseAjaxServlet.java:

    import java.io.IOException;        import java.util.Date;        import java.util.Random;                 import javax.servlet.ServletException;        import javax.servlet.http.HttpServletRequest;        import javax.servlet.http.HttpServletResponse;                 import org.codehaus.jettison.json.JSONArray;        import org.eclipse.jetty.websocket.WebSocket;        import org.eclipse.jetty.websocket.WebSocketServlet;                 /**        * @author Mathieu Carbou (mathieu.carbou@gmail.com)        */        public final class ReverseAjaxServlet extends WebSocketServlet {                     private final Endpoints endpoints = new Endpoints();            private final Random random = new Random();            private final Thread generator = new Thread("Event generator") {                @Override                public void run() {                    while (!Thread.currentThread().isInterrupted()) {                        try {                            Thread.sleep(random.nextInt(5000));                            endpoints.broadcast(new JSONArray().put("At " + new Date()).toString());                        } catch (InterruptedException e) {                            Thread.currentThread().interrupt();                        }                    }                }            };                     @Override            public void init() throws ServletException {                super.init();                generator.start();            }                     @Override            public void destroy() {                generator.interrupt();                super.destroy();            }                         @Override            public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {                return endpoints.newEndpoint();            }                     @Override            protected void doGet(HttpServletRequest req, HttpServletResponse resp)                    throws ServletException, IOException {                resp.getWriter().write("11111");            }        }  

Endpoints.java:
    package com.cn.test.chapter2.websocket;                import org.eclipse.jetty.websocket.WebSocket;                import java.util.Queue;        import java.util.concurrent.ConcurrentLinkedQueue;                 /**        * @author Mathieu Carbou (mathieu.carbou@gmail.com)        */        final class Endpoints {            private final Queue<Endpoint> endpoints = new ConcurrentLinkedQueue<Endpoint>();                     void broadcast(String data) {        //        for (Endpoint endpoint : endpoints) {        //            endpoint.onMessage(data);        //        }            }                     void offer(Endpoint endpoint) {                endpoints.offer(endpoint);            }                     void remove(Endpoint endpoint) {                endpoints.remove(endpoint);            }                     public WebSocket newEndpoint() {                return new Endpoint(this);            }        }  

Endpoint.java
    import java.io.IOException;        import java.util.concurrent.ConcurrentLinkedQueue;                                         import org.codehaus.jettison.json.JSONArray;        import org.eclipse.jetty.websocket.WebSocket;                 /**        * @author Mathieu Carbou (mathieu.carbou@gmail.com)        */        class Endpoint implements WebSocket.OnTextMessage  {                     protected Connection _connection;                         private Endpoints endpoints;                         private static int clientCounter = 0;            private int clientId = clientCounter++;                         public Endpoint(Endpoints endpoints) {                this.setEndpoints(endpoints);            }                         @Override            public void onClose(int code, String message) {                System.out.println("Client disconnected");                                  this.endpoints.remove(this);            }                     @Override            public void onOpen(Connection connection) {                System.out.println("Client connected");                  _connection = connection;                try {                    this._connection.sendMessage(new JSONArray().put("ClientID = " + clientId).toString());                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                endpoints.offer(this);            }                     @Override            public void onMessage(final String data) {                System.out.println("Received data: " + data);                  this.endpoints.broadcast(data);            }                     public Endpoints getEndpoints() {                return endpoints;            }                     public void setEndpoints(Endpoints endpoints) {                this.endpoints = endpoints;            }        }  

 

 

辅助工具:

        在编写服务器最麻烦的是要写对应的客户端来测试,还好Chrome为我们解决了这个问题。下载Chrome插件WebSocket Clinet可以轻松地和服务器建立连接,发送消息到服务器。

来自:http://blog.csdn.net/lrenjun/article/details/39934823