The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Writing a TCP/UDP stack supporting the SIP protocol using the Grizzly Framework

Posted by jfarcand on February 12, 2008 at 9:53 AM PST

Over the last year, I've had the opportunity to work (and learn!) with Ericsson on a TCP/UDP stack for supporting the SIP protocol. SIP is an interesting protocol and implementing support for it with Grizzly was quite interesting. My goal over the next few weeks is to explain how I did it using the Grizzly framework.

First, if you are new with Grizzly, I recommend you take a look at the terminology we are using. You might also wants to read about what is the SIP protocol, although for this first entry, I will focus on the transport implementation, not on SIP yet.

The first step when writing an application using Grizzly is to create the com.sun.grizzly.Controller object. This object is THE object that allow you to control the framework.

Controller controller = new Controller();

Next, we need to configure which transport we want to support. For this blog purpose, I will only explain how to support UDP and TCP, and talk about TLS later. In Grizzly, a transport is represented using the SelectorHandler interface, and the framework support by default three implementation: TCPSelectorHandler, TLSSelectorHandler and UDPSelectorHandler. By default, the Controller support TCP, but to help understanding how it works, the code below will explicitly configure the Controller to support TCP and UDP:

        TCPSelectorHandler tcpSelector = new TCPSelectorHandler();
        tcpSelectorHandler.setPort(8080);
        controller.addSelectorHandler(tcpSelector);
        
        UDPSelectorHandler udpSelector = new UDPSelectorHandler();
        udpSelectorHandler.setPort(8080);           
        controller.addSelectorHandler(udpSelector);

So far so good :-) Next is to configure the thread pool we want our application to use. In Grizzly, it is possible to associate a thread pool per SelectorHandler or shared among SelectorHandler. By default, the Controller creates a shared thread pool. For this application, we gonna create a single thread pool (called Pipeline) shared between the TCPSelectorHandler and UDPSelectorHandler.

        Pipeline mySharedPipeline = new DefaultPipeline());
        pipeline.setMaxThreads(5);
        
        controller.setPipeline(mySharedPipeline);

The next optional object to configure is the ProtocolChainInstanceHandler. The way Grizzly works is when a connection is made to one of its SelectorHandler, it accepts the connection (OP_ACCEPT) and then delegate the handling of the read/write/connect operations to what we call a ProtocolChain. A ProtocolChain implement the "Chain of Responsibility" pattern (for more info, take a look at the classic "Gang of Four" design patterns book). Towards that end, the Chain API models a computation as a series of "protocol filter" that can be combined into a "protocol chain". The API for ProtocolFilter consists of a two methods (execute(Context) and postExecute(Context)) which is passed a "protocol context" parameter containing the dynamic state of the computation, and whose return value is a boolean that determines whether or not processing for the current chain has been completed (false), or whether processing should be delegated to the next ProtocolFilter in the chain (true).

ProtocolFilter can be stateless or stateful. By stateless, I mean an instance of ProtocolFilter can be executed by several threads simultaneously. By stateful, I means an instance of ProtocolFilter is not thread safe, so we usually need to create one instance per thread. Based on what your application does, you might want to configure the "state" of you ProtocolChain: stateful or stateless. This is what the ProtocolChainInstanceHandler is for. The Controller will invoke that object every time it needs to execute an operation. For our application, let's assume we define a stateless ProtocolChain/ProtocolFilter:

        ProtocolChainInstanceHandler pciHandler =
                new ProtocolChainInstanceHandler() {

            final private ProtocolChain protocolChain = new DefaultProtocolChain();

            public ProtocolChain poll() {
                return protocolChain;
            }

            public boolean offer(ProtocolChain instance) {
                return true;
            }
        };
        controller.setProtocolChainInstanceHandler(pciHandler);

Next, we need to add our protocol specific implementation via ProtocolFilter. Grizzly ships with a ReadFilter than can be used to read bytes, independently of the transport used (UDP or TCP). Mainly, the ReadFilter reads the available bytes, and pass the control over the next ProtocolFilter. For the purpose of this blog, let's add an echo ProtocolFilter (EchoFilter) that return the bytes sent by the client:

        ProtocolChain protocolChain = pciHandler.poll();
        protocolChain.addFilter(new ReadFilter());
        protocolChain.addFilter(new EchoFilter());

Technically, the EchoFilter just do:

    public boolean execute(Context ctx) throws IOException {
        final WorkerThread workerThread = ((WorkerThread)Thread.currentThread());
        ByteBuffer buffer = workerThread.getByteBuffer();
        buffer.flip();
        if (buffer.hasRemaining()) {
            // Depending on protocol perform echo
            SelectableChannel channel = ctx.getSelectionKey().channel();
            try {
                if (ctx.getProtocol() == Controller.Protocol.TCP) { // TCP case
                    OutputWriter.flushChannel(channel, buffer);
                } else if (ctx.getProtocol() == Controller.Protocol.UDP) {  // UDP case
                    DatagramChannel datagramChannel = (DatagramChannel) channel;
                    SocketAddress address = (SocketAddress) 
                            ctx.getAttribute(ReadFilter.UDP_SOCKETADDRESS);
                    OutputWriter.flushChannel(datagramChannel, address, buffer);
                }
            } catch (IOException ex) {
                // Store incoming data in byte[]
                byte[] data = new byte[buffer.remaining()];
                int position = buffer.position();
                buffer.get(data);
                buffer.position(position);
                
                Controller.logger().log(Level.WARNING, 
                        "Exception. Echo \"" + new String(data) + "\"");
                throw ex;
            }
        }
        
        buffer.clear();
        return false;
    }

Without going into too much details, this ProtocolFilter just take the ByteBuffer that contains the client's bytes, and write it back using a temporary Selector trick (kind of a blocking write operation). Finally, we are ready to start our application:

controller.start();

There is other configurable interfaces with Grizzly (like how idles connections are closed using a SelectionKeyHandler), but for now we gonna just use the default. That's it, we have now a server that listen for UDP and TCP requests :-). To recap, here is complete source code:

package com.sun.grizzly.utils;
      1 package com.sun.grizzly.utils;
      2 
      3 import com.sun.grizzly.Controller;
      4 import com.sun.grizzly.DefaultPipeline;
      5 import com.sun.grizzly.Pipeline;
      6 import com.sun.grizzly.ProtocolChain;
      7 import com.sun.grizzly.ProtocolChainInstanceHandler;
      8 import com.sun.grizzly.TCPSelectorHandler;
      9 import com.sun.grizzly.UDPSelectorHandler;
     10 import com.sun.grizzly.filter.EchoFilter;
     11 import com.sun.grizzly.filter.ReadFilter;
     12 
     13 public class SipServerDemo {
     14 
     15     public void startSIPServerDemo(){
     16 
     17         Controller controller = new Controller();
     18 
     19         TCPSelectorHandler tcpSelector = new TCPSelectorHandler();
     20         tcpSelectorHandler.setPort(8080);
     21         controller.addSelectorHandler(tcpSelector);
     22 
     23         UDPSelectorHandler udpSelector = new UDPSelectorHandler();
     24         udpSelectorHandler.setPort(8080);
     25         controller.addSelectorHandler(udpSelector);
     26 
     27         Pipeline mySharedPipeline = new DefaultPipeline());
     28         pipeline.setMaxThreads(5);
     29 
     30         controller.setPipeline(mySharedPipeline);
     31 
     32         ProtocolChainInstanceHandler pciHandler =
     33                 new ProtocolChainInstanceHandler() {
     34 
     35             final private ProtocolChain protocolChain = new DefaultProtocolChain();
     36 
     37             public ProtocolChain poll() {
     38                 return protocolChain;
     39             }
     40 
     41             public boolean offer(ProtocolChain instance) {
     42                 return true;
     43             }
     44         };
     45         controller.setProtocolChainInstanceHandler(pciHandler);
     46 
     47         ProtocolChain protocolChain = pciHandler.poll();
     48         protocolChain.addFilter(new ReadFilter());
     49         protocolChain.addFilter(new EchoFilter());
     50 
     51         controller.start();
     52     }
     53 
     54 }

Wow, that not that complicated :-) You can browse the Grizzly code online if you are interested about Grizzly's internal here. My next blog will describe how to implement a client side TCP/UDP connector using the ConnectorHandler (needed by the SIP protocol), and discuss the details of how the SIP protocol can be implemented via a ProtocolFilter. A+

Part II can be read here.

technorati:

Related Topics >> Glassfish      
Comments
Comments are listed in date ascending order (oldest first)

Hello, Thanks for those valourous clarifications on the use of Grizzly to implement a transport application. I just discover Grizzly today because I was looking for a NIO application because I aim to implement a probe with UDP transport, and for that purpose I need to know how to access to the bytes read by the ReadFilter to interpret for example the response of a client.

Bonjour JF. I am trying to create my one protocolparserfilter but I can't find the package for ProtocolParser. Could you help me please! thanks

Some more precision for my request: I made: import com.sun.grizzly.ProtocolParser; but it is not accepted. The external Jar that I add is grizzly-framework-1.7.3.2.jar THANK YOU

Thanks, it exactly answers my question. I'm looking forward to your next instalment to this tutorial. I am most interested in knowing how to pass the received bytes to the business logic tier. As a current MINA user, I am most interested in knowing if Grizzly has a construct similar to Mina's IoHandlerAdapter (http://mina.apache.org/report/trunk/xref/org/apache/mina/common/IoHandle...). This IoHandlerAdapter construct is a good high level demarcation point between business logic tier and I/O related logic. Thanks.

Salut,[ProtocolChain] -> It depends on which strategy you want to implement. You can associate one ProtocolChain per Request (the statefull approach), or one ProtocolChain shared amongs all requests (stateless approach). The ProtocolChain can be shared amongs all SelectorHandler if you do:Controller.setProtocolChainInstanceHandler() or you can set it per SelectorHandler:SelectorHandler.setProtocolChainInstanceHandler()So to answer your last question, your don't need to have 2 Controller, just need to create two instance of a ProtocolChainInstanceHandler, and set them on the SelectorHandler handling cA and cB :-)Thanks!

Hi Sebastien, The best way to start is to subscribe to the users@grizzly.dev.java.net where the community can help you very fast to start. Do you mind sending your request there? If you can't, ping me again and I will add the information here, but I would really like to have the community (including me :-)) to answer :-) This is easy to implement what you want with Grizzly :-) Thanks! -- Jeanfrancois

Great tutorial! Things are starting to get clearer with Grizzly. Thanks. Correct me if I'm wrong. There is a one-to-one relationship between a protocolChainInstanceHandler and a Controller? This means that for a given server, we can listen to many ports using many transport protocols, but the information (the bytes) received will all be handled by the same protocolChain. My interrogation is motivated by the following: I want to listen to port pA and handle the information received using protocolChain cA and listen to port pB using protocolChain cB. Would you recommend creating 2 controllers? If so, can I share a common thread pool (pipeline) between the two? Thanks... Guess I should have asked this on the forum... thanks anyway.

Bonjour JF. I'm currently looking for an alternative for our current TCP/UPP connector in our gateways at my jobs. Most of our network connections handling are done on home made stuff, made few years ago. I saw few options, create again our own librairies (don't want to), Mina and Grizzly. I really want to try Grizzly, but I don't know where to start. You seem to have the only good forum for grizzly :) Your example seem to do the job for a starting point, but can you help me by giving me few tips are ? How do I create a Basic server (like your echoserver) that will accept multiple connections make from non NIO clients (like telnet) ? I will like to have the blocking and non blocking option (for our legacy gateways, they wait until a timeout). merci Sébastien

The way Grizzly is designed differ from the IoHandlerAdapter/IoSession. In Grizzly, you will find the same set of functionalities by using the Context , which is passed to a ProtocolFilter. This is probably a bad association, but it seems the ProtocolFilter is something similar to the IoHandler, and the IoSession similar to the Context. But long time I looked at MINA so my understanding is probably wrong. Anyway you have the same info with Context/ProtocolFilter, and we could probably add to the framework something similar to Mina where Grizzly pushes some events to a specific method from a ProtocolFilter. We just need to add an interface on top of the ProtocolFilter...will think of it :-) Thanks!

I wouldn't bother with the Euro spec turbo's, you won't gain a lot for the hassle. However a hybridised pair of stock turbo's might be worth it. They are the same externally as the stock turbo's but feature larger compressor and turbine wheels. The 550cc injectors will allow you to get close to 550HP with the right turbo's but in reality a small single turbo is required to achieve the power as even the hybrid OEM turbos arn't capable of flowing that much air.how to play roulette roulette how to win roulette tips how to win at roulette Run your car on water Run Car on Water Water Powered Car Water Car horse racing horse racing tips horse racing betting horse racing software horse racing systems downloadable movies download full version movies download movies movie downloads forex trading systems forex currency trading forex software learn forex world of warcraft guide wow gold guide wow guide world of warcraft cheats how to play poker learn poker poker strategy poker sites poker calculator free blackjack online blackjack blackjack strategy how to play blackjack iphone downloads iphone games download iphone download site review iphone download sites how to make money on ebay make money on ebay warren buffett warren buffet

Where is the stack for filtering the SIP messages.Do you have the full source code in samples or somewhere we can download and study.