一步步學WebSocket(2)編程式WebSocket

上篇,這篇咱們採用編程式WebSocket實現上篇的例子:java


服務端Endpoint,再也不使用ServerEndpoint註解:編程

public class ProgramerServer extends Endpoint {
    @Override
    public void onOpen(Session session, EndpointConfig edc) {
        System.out.println("Somebody is coming!");
        
        session.addMessageHandler(new MessageHandler.Whole<String>() {

            @Override
            public void onMessage(String message) {
                System.out.println(message); 
                try {
                    session.getBasicRemote().sendText("it is sickening");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

   @Override
   public void onClose(Session session, CloseReason closeReason) {
        // NO-OP by default
    }
   @Override
    public void onError(Session session, Throwable throwable) {
        // NO-OP by default
    }
}

而是繼承一個Endpoint抽像類,咱們發現Endpoint提供的三個方法:onOpen,onClose,onError。後端

與在聲明式WebSocket中存在的四件套:@OnOpen,@OnClose,@OnMessage , @OnError, 相比少了@OnMessage。session

那收到消息以後回調什麼呢? 從上面的代碼能夠看到爲session增長的MessageHandler有一個類似方法onMessage。對,就是他。接收到消息爲調用的就是這個handler的onMessage方法。ide


難道兩種編程方式的運行邏輯還不相同? 其實否則,對於聲明式編程,也是經過MessageHandler回調@OnMessage標記的方法。只是這個過程在聲明式編程模式中,被Tomcat等做了包裝。ui


(這裏透一點,對於聲明式編程, Tomcat都會將其轉換成本篇的這種模式, 聲明式編程中POJO沒有繼承Endpoint抽像類,Tomcat自已構造一個Endpoint的子類,在Tomcat8中叫PojoEndpointServer。以下繼承關係:url

public class PojoEndpointServer extends PojoEndpointBase
public abstract class PojoEndpointBase extends Endpoint.

後端的運行採用PojoEndpointServer委託給咱們的POJO類就能夠,一樣道理code

@ClientEndpoint註解的POJO對應到PojoEndpointClient。)server


發現沒,沒有ServerEndpoint註解, 沒法配置端點的映射路徑? 這裏咱們須要聲明一個ServerApplicationConfig實體(還記和Restful WS中的那個javax.rs.ws.core.Application嗎?)來完成這個功能:blog

public class MyApplicationConfig implements ServerApplicationConfig{

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> allClasses) {
        return null;
    }

    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> end) {
        ServerEndpointConfig sec =  ServerEndpointConfig.Builder.create(ProgramerServer.class, "/chat")
        .configurator(new ServerEndpointConfig.Configurator(){
        }).build();
        
        
       return new HashSet<ServerEndpointConfig>(){{
           add(sec);
       }};
    }
}

getEndpointConfig構建了一個ServerEndpointConfig集合,上一篇聲明式WebSocket爲何不須要這個? 一樣須要,只是在聲明式WebSocket中Tomcat能夠經過@ServerEndpoint註解去構建他。參看Tomcat代碼:

 @Override
    public void addEndpoint(Class<?> pojo) throws DeploymentException {

        ServerEndpoint annotation = pojo.getAnnotation(ServerEndpoint.class);
      
        // ServerEndpointConfig
        ServerEndpointConfig sec;
        Class<? extends Configurator> configuratorClazz =
                annotation.configurator();
        Configurator configurator = null;
        if (!configuratorClazz.equals(Configurator.class)) {
            try {
                configurator = annotation.configurator().newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new DeploymentException(sm.getString(
                        "serverContainer.configuratorFail",
                        annotation.configurator().getName(),
                        pojo.getClass().getName()), e);
            }
        }
        sec = ServerEndpointConfig.Builder.create(pojo, path).
                decoders(Arrays.asList(annotation.decoders())).
                encoders(Arrays.asList(annotation.encoders())).
                subprotocols(Arrays.asList(annotation.subprotocols())).
                configurator(configurator).
                build();

        addEndpoint(sec);
    }

Tomcat爲每個ServerEndpoint構造了一個ServerEndpointConfig。


將上面兩個類同時,打入War包,部署到Tomcat上,一個WebSocket服務端就OK了。


如今你能夠用上篇的Client去訪問這個WebSocket。或者你已厭倦了Annocation. 來一個編程式Client吧:

public class ProgramerClient extends Endpoint {

    @Override
    public void onOpen(Session session, EndpointConfig edc) {
        System.out.println("I was accpeted by her!");
        session.addMessageHandler(new MessageHandler.Whole<String>() {

            @Override
            public void onMessage(String message) {
                System.out.println("she say: " + message); 
            }
        });
    }

}

爲何沒有onClose,onError方法? 由於Endpoint中有默認實現,這裏就沒有重載。

public class Client {
    public static void main(String[] args) throws DeploymentException, IOException, InterruptedException {

        WebSocketContainer ws = ContainerProvider.getWebSocketContainer();
        String url = "ws://localhost:8080/ChatWeb2/chat";
        
        ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(new MyClientConfigurator()).build();
        Session session = ws.connectToServer(ProgramerClient.class,cec,URI.create(url));
        session.getBasicRemote().sendText("Hello,chick!");
        Thread.currentThread().sleep(10000);
    }
}

等等,有點不一樣。固然了,這裏沒有了ClientEndpoint,固然也就沒有了@ClientEndpoint.Configurator字段(還記得@ClientEndpoint的結構嗎?)

固然也就沒有了ClientEndpointConfig。因此須要咱們自已加一個。


能夠看出編程式WebSocket端點比Annotation複雜了不少。採用Annotation提示使用編程變得簡單,

而對於WebSocket容器(即本文的Tomcat等)則須要將這種Annotation提示轉換成執行代碼。


爲了你們對兩種模式有個總體的認識,中間的細節咱們都跳過了。但願不會對你們的理解帶來障礙。

相關文章
相關標籤/搜索