程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Tomcat和Jetty對WebSocket的支持

Tomcat和Jetty對WebSocket的支持

編輯:C++入門知識

Tomcat和Jetty對WebSocket的支持


公司項目需要,了解了下目前幾種支持WebSocket的框架。以前用jWebSocket做過一些項目,相對來說,改jWebSocket的源碼略復雜,也不是一天兩天能搞定的。一調研才發現,現在很多主流的web框架都已經開始支持WebSocket了,不得不感慨時間太快,科技進步太快,在微策略的幾年真的荒廢了。不多說,先記錄下今天的研究。


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 ([email protected])
 */
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 ([email protected])
 */
final class Endpoints {
    private final Queue endpoints = new ConcurrentLinkedQueue();
 
    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 ([email protected])
 */
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可以輕松地和服務器建立連接,發送消息到服務器。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved