Home > Uncategorized > Writing a TCP/UDP stack supporting the SIP protocol using the Grizzly Framework, part II

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

Back to technical blog after “buzzwording” for too long :-). Time to resume the tutorial about Grizzly and SIP. The first part was explaining Grizzly’s server side components, this time I will concentrate on the client components. Like for the first part, I will not yet jump into the details of the SIP protocol.

IMG_0300.JPG

As for the server side, you first need to create the entry point in Grizzly, which is the Controller.

 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 ConnectorHandler interface, and the framework support by default three implementation: TCPConnectorHandler, TLSConnectorHandler and UDPConnectorHandler. 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:

ConnectorHandler tcpConnector = controller.acquireConnectorHandler(Controller.Protocol.TCP);

ConnectorHandler udpConnector = controller.acquireConnectorHandler(Controller.Protocol.UDP);

Next we need to decide if we want to use a ProtocolChain for handling our request/response, or use a CallbackHandler to experiment with the bytes and the state of the connection. Since SIP is a two way protocol (the client can become the server and vice versa), let’s first demonstrate how to use a ConnectorHandler with a ProtocolChain. For the purpose of this blog, let’s add a (LogFilter) that output the bytes sent by the server:

ProtocolChain protocolChain = controller.getProtocolChain();
protocolChain.addFilter(new ReadFilter());
protocolChain.addFilter(new LogFilter());

That means that once we are connected to a remote server, all the bytes we are getting back will be processed by the ReadFilter and LogFilter. So let’s connect:

tcpConnector.connect(new InetSocketAddress("localhost",8080),null);
tcpConnector.write(ByteBuffer.wrap(sipRequestBytes));

What is interesting here is the ProtocolChain used for reading the bytes can also be used on the server side. So let’s say we wrote a SipParserProtocolFilter, we can use that ProtocolFilter for parsing the bytes send by a remote client and also we can use it to parse the response we are getting when connecting to a remote server:


      1 package com.sun.grizzly.utils;
      2 import com.sun.grizzly.ConnectorHandler;
      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.LogFilter;
     11 import com.sun.grizzly.filter.ReadFilter;
     12 
     13 public class SipDemo {
     14 
     15     public void startSIPServerDemo(){
     16 
     17         Controller controller = new Controller();
     18 
     19         TCPSelectorHandler tcpSelector = new TCPSelectorHandler();
     20         tcpSelector.setPort(8080);
     21         controller.addSelectorHandler(tcpSelector);
     22 
     23         UDPSelectorHandler udpSelector = new UDPSelectorHandler();
     24         udpSelector.setPort(8080);
     25         controller.addSelectorHandler(udpSelector);
     26 
     27         Pipeline mySharedPipeline = new DefaultPipeline();
     28         mySharedPipeline.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 LogFilter());
     50 
     51         controller.start();
     52         ConnectorHandler ch = controller.acquireConnectorHandler(Controller.Protocol.TCP);
     53         ch.connect(new InetSocketAddress("localhost",8080),(CallbackHandler)null);
     54     }
     55 
     55 }

Graphically, it looks like the following:

demo1.jpg

Now let’s assume we want to use the ConnectorHandler for read and write operations, but this time without using a ProtocolChain. The component we can use in that case is called a CallbackHandler. In the example above, we haven’t created any CallbackHandler, but under the hood, Grizzly created one (the DefaultCallbackHandler), which dispatch all connect, read and write operations to the ProtocolChain. By default, the CallbackHandler interface, which is invoked by the Controller every time there is an asynchonous operations ready to be processed, looks like:

 41 /**
 42  * Callback handler for non blocking client operations.
 43  *
 44  * @param E  object containing information about the current
 45  *        non blocking connection
 46  * @author Jeanfrancois Arcand
 47  */
 48 public interface CallbackHandler extends Handler{
 49 
 50     /**
 51      * This method is called when an non blocking OP_CONNECT is ready
 52      * to get processed. This method must invoke ConnectorHandler.finishConnect()
 53      * to complete the connection operations.
 54      *
 55      * @param ioEvent an object containing information about the current
 56      *        non blocking connection.
 57      */
 58     public void onConnect(IOEvent ioEvent);
 59 
 60 
 61     /**
 62      * This method is called when an non blocking OP_READ is ready
 63      * to get processed.
 64      * @param ioEvent an object containing information about the current
 65      *        non blocking connection.
 66      */
 67     public void onRead(IOEvent ioEvent);
 68 
 69 
 70     /**
 71      * This method is called when an non blocking OP_WRITE is ready
 72      * to get processed.
 73      * @param ioEvent an object containing information about the current
 74      *        non blocking connection.
 75      */
 76     public void onWrite(IOEvent ioEvent);
 77 
 78 }

As an example, here is a simple implementation of a CallbackHandler connect, read and write operations:

274             public void onConnect(IOEvent ioEvent) {
275                 SelectionKey key = ioEvent.attachment().getSelectionKey();
276                 try{
277                     tcpConnector.finishConnect(key);
278                 } catch (IOException ex){
279                     ex.printStackTrace();
280                 }   
281                 // Register for read events
282                 ioEvent.attachment().getSelectorHandler().register(key,
283                         SelectionKey.OP_READ);
284             }
285             
286             public void onRead(IOEvent ioEvent) {
287                 SelectionKey key = ioEvent.attachment().getSelectionKey();
288                 SelectorHandler selectorHandler = ioEvent.attachment().
289                         getSelectorHandler(); 
290                 SocketChannel socketChannel = (SocketChannel)key.channel();
291                 
292                 try {
293                     int nRead = socketChannel.read(readBB);
294                     // Do something with the bytes
295                     // ...
296                     // Ask for more bytes
297                     selectorHandler.register(key, SelectionKey.OP_READ);
298                 } catch (IOException ex){
299                     selectorHandler.getSelectionKeyHandler().cancel(key);
300                 }   
301             }   
302 
303             public void onWrite(IOEvent ioEvent) {
304                 SelectionKey key = ioEvent.attachment().getSelectionKey();
305                 SelectorHandler selectorHandler = ioEvent.attachment().
306                         getSelectorHandler();
307                 SocketChannel socketChannel = (SocketChannel)key.channel();
308                 try{
309                     while(writeBB.hasRemaining()){
310                         int nWrite = socketChannel.write(writeBB);
311                         
312                         if (nWrite == 0){
313                             selectorHandler.register(key, SelectionKey.OP_WRITE);
314                             return;
315                         }   
316                     }
317                 } catch (IOException ex){
318                     selectorHandler.getSelectionKeyHandler().cancel(key);
319                 }   
320 
321             }

With the example above, you can always decide to delegate the processing of the bytes to your ProtocolChain, which wasn’t the case with my first example. Graphically, it looks like:

demo3.jpg

To configure your CallbackHandler, you just need to pass an instance when executing the asynchronous connect operation:

tcpConnector.connect(new InetSocketAddress("localhost",8080),new MyConnectorHandler());

The only difference here is instead of passing a null, we are passing an instance of CallbackHandler. What I like with CallbackHandler is that I can decide when I want to delegate the processing to a ProtocolChain, and completely ignore that component if I don’t want to write ProtocolFilter. And when I write a non blocking client, I can always decide to either write a CallbackHandler, or a simple ProtocolFilter.

OK, hopefully the next blog for that serie will happens sooner. Next time I will start digging about SIP and how we have implemented it in Sailfin.

_uacct = “UA-3111670-1″;
urchinTracker();

technorati:

About these ads
Categories: Uncategorized
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 50 other followers

%d bloggers like this: