Tomcat和Jetty對WebSocket的支持

公司項目需要,瞭解了下眼下幾種支持WebSocket的框架。曾經用jWebSocket作過一些項目。相對來講。改jWebSocket的源代碼略複雜,也不是一天兩天能搞定的。

一調研才發現,現在很是多主流的web框架都已經開始支持WebSocket了,不得不感慨時間太快,科技進步太快,在微策略的幾年真的荒廢了。很少說,先記錄下今天的研究。java



Tomcat:web

       J2EE如下用的最多的容器應該就是tomcat了。說到tomcat對WebSocket的支持,不得不先提一下,眼下的WebSocket協議已經通過了好幾代的演變。不一樣瀏覽器對此協議的支持程度也不一樣。所以,假設做爲server。最理想的是支持儘量多的WebSocket協議版本號。json

tomcat8真正支持jsr-356(包括對websocket的支持)。 tomcat7支持部分版本號的websocket實現不兼容jsr-356。所以,能用tomcat8的話,仍是儘可能用。瀏覽器


代碼實現至關簡單,下面是一個列子,僅僅需要tomcat8的基本庫,不需要其它依賴。tomcat

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:websocket

       Jetty和Tomcat同樣,也是一個Servlet的容器。假設說不一樣之處,那麼最大的不一樣應該是Tomcat採用的是BIO處理方式,也就是說一個request會用一個線程去處理,即便是WebSocket這樣的長鏈接,也是會獨立開一個線程。session

做爲一個普通的Webserver,tomcat可以輕鬆應對耗時比較短的Request/Response。但是假設換成是長鏈接的WebSocket。那麻煩就來了,對於上萬用戶的聊天和推送,總不能開上萬個線程去處理吧。此時,Jetty的性能就體現出來了。Jetty採用的是NIO,一個線程可以處理多個WebSocket的長連接,假設你的需求是大量耗時比較長的request或者大量長鏈接,那麼建議採用Jetty。框架


        Jetty對WebSocket的實現有點繞,Servlet再也不是繼承原來的HttpServlet。而是繼承WebSocketServlet。此處要注意導入jetty-util.jar和jetty-websocket.jar兩個包,不然可能會有class not found錯誤。dom

ReverseAjaxServlet.java:eclipse

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;
    }
}




輔助工具:

        在編寫server最麻煩的是要寫相應的client來測試,還好Chrome爲咱們攻克了這個問題。下載Chrome插件WebSocket Clinet可以輕鬆地和server創建鏈接,發送消息到server。

相關文章
相關標籤/搜索