2012-06-06 8 views
6

Próbuję użyć netty przez clojure. Mogę uruchomić serwer, ale nie uda się zainicjować akceptowanego gniazda. Poniżej znajdują się odpowiednio komunikat o błędzie i kod. Czy ktoś wie, co jest/lub może być nie tak? Wydaje mi się, że problem dotyczy numeru (Channels/pipeline (server-handler)).Java Interop - Netty + Clojure

Komunikat o błędzie

#<NioServerSocketChannel [id: 0x01c888d9, /0.0.0.0:843]> 
Jun 6, 2012 12:15:35 PM org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink 
WARNING: Failed to initialize an accepted socket. 
java.lang.IllegalArgumentException: No matching method found: pipeline 

project.clj

(defproject protocol "1.0.0-SNAPSHOT" 
    :description "Upload Protocol Server" 
    :dependencies [ 
    [org.clojure/clojure "1.2.1"] 
    [io.netty/netty "3.4.5.Final"]]) 

core.clj

(ns protocol.core 
    (:import (java.net InetSocketAddress) 
      (java.util.concurrent Executors) 
      (org.jboss.netty.bootstrap ServerBootstrap) 
      (org.jboss.netty.channel Channels ChannelPipelineFactory SimpleChannelHandler) 
      (org.jboss.netty.channel.socket.nio NioServerSocketChannelFactory) 
      (org.jboss.netty.buffer ChannelBuffers))) 

(def policy 
    "<content>Test</content>") 


(defn server-handler 
    "Returns netty handler." 
    [] 
    (proxy [SimpleChannelHandler] [] 
     (messageReceived [ctx e] 
      (let [ch (.getChannel e)] 
       (.write ch policy) 
       (.close ch))) 

     (channelConnected [ctx e] 
      (let [ch (.getChannel e)] 
       (.write ch policy) 
       (.close ch))) 

     (exceptionCaught [ctx e] 
      (let [ex (.getCause e)] 
       (println "Exception" ex) 
       (-> e .getChannel .close))))) 

(defn setup-pipeline 
    "Returns channel pipeline." 
    [] 
    (proxy [ChannelPipelineFactory] [] 
     (getPipeline [] 
      (Channels/pipeline (server-handler))))) 

(defn startup 
    "Starts netty server." 
    [port] 
    (let [channel-factory (NioServerSocketChannelFactory. (Executors/newCachedThreadPool) (Executors/newCachedThreadPool)) 
      bootstrap (ServerBootstrap. channel-factory)] 
     (.setPipelineFactory bootstrap (setup-pipeline)) 
     (.setOption bootstrap "child.tcpNoDelay" true) 
     (.setOption bootstrap "child.keepAlive" true) 
     (.bind bootstrap (InetSocketAddress. port)))) 

Odpowiedz

6

Istnieją trzy problemy z kodem:

  1. Współpraca Java z metodą vararg Channels.channel(). można utworzyć wektor modułów obsługi kanałów i zawijać go za pomocą (into-array ChannelHandler ..)

  2. Nie można pisać obiektów typu String bezpośrednio na kanale Netty. musisz najpierw napisać ciąg do ChannelBuffer i zapisać ten bufor lub użyć StringCodecHandler.

  3. Pisanie do kanału Netty jest asynchronus, więc nie można go natychmiast zamknąć. musisz zarejestrować przyszłego słuchacza i zamknąć kanał po jego zakończeniu.

Oto działający kod.

(ns clj-netty.core 
    (:import (java.net InetSocketAddress) 
      (java.util.concurrent Executors) 
      (org.jboss.netty.bootstrap ServerBootstrap) 
      (org.jboss.netty.buffer ChannelBuffers) 
      (org.jboss.netty.channel Channels ChannelFutureListener ChannelHandler ChannelPipelineFactory SimpleChannelHandler) 
      (org.jboss.netty.channel.socket.nio NioServerSocketChannelFactory) 
      (org.jboss.netty.buffer ChannelBuffers))) 


(def policy 
    (ChannelBuffers/copiedBuffer 
    (.getBytes "<content>Test</content>"))) 


(defn server-handler 
    "Returns netty handler." 
    [] 
    (proxy [SimpleChannelHandler] [] 
    (messageReceived [ctx e] 
     (let [ch (.getChannel e)] 
     (.addListener 
      (.write ch policy) 
      (ChannelFutureListener/CLOSE)))) 

    (channelConnected [ctx e] 
     (let [ch (.getChannel e)] 
     (.addListener 
      (.write ch policy) 
      (ChannelFutureListener/CLOSE)))) 

    (exceptionCaught [ctx e] 
     (let [ex (.getCause e)] 
     (println "Exception" ex) 
     (-> e .getChannel .close))))) 

(defn setup-pipeline 
    "Returns channel pipeline." 
    [] 
    (proxy [ChannelPipelineFactory] [] 
    (getPipeline [] 
     (let [handler (server-handler)] 
     (Channels/pipeline (into-array ChannelHandler [handler])))))) 



(defn startup 
     "Starts netty server." 
     [port] 
     (let [channel-factory (NioServerSocketChannelFactory. (Executors/newCachedThreadPool) (Executors/newCachedThreadPool)) 
      bootstrap (ServerBootstrap. channel-factory)] 
     (.setPipelineFactory bootstrap (setup-pipeline)) 
     (.setOption bootstrap "child.tcpNoDelay" true) 
     (.setOption bootstrap "child.keepAlive" true) 
     (.bind bootstrap (InetSocketAddress. port)))) 

Wystarczy popatrzeć na Aleph (również używa Netty), który może być używany do budowy klientów i serwerów w wielu różnych protokołów z ładnym Clojure API.

+0

Dzięki za jasne i szczegółowe wyjaśnienie, było bardzo pomocne. Z pewnością planuję spróbować Aleph, ale pomyślałem, że dostanę dwie ptaszki z jednym kamieniem - naucz się netty, ćwicząc clojure. – Ari