Quick Start
Resip's Fundamental Design
The resip stack plays the role of the UAC/UAS core, and the app plays the role of the Transaction User (or TU), both as described in RFC 3261. For the unfamiliar, the TU sends and receives SIP messages from the core, while the core handles details like retransmissions, timeouts, and interactions with the transport layer. The UAS/UAC core will also inform the TU when transactions end.
Most applications do not write a TU directly; they use the Dialog Usage Manager, which is a TU that manages dialogs, registrations, subscriptions and authentication for them.
Resip's Other Responsibilities
In addition to satisfying the duties of the UAS/UAC core, resip provides additional functionality, such as:
- SIP message parsing : Resip supplies parse-code for all the core SIP grammar elements (this parse code is fairly liberal, and does not do deep validation).
- SIP message construction : Resip supplies code for constructing SIP messages according to the rules in RFC 3261.
- RFC 3263 DNS logic : The resip stack carries out all RFC 3263 DNS lookup logic for you. This is actually a non-trivial task, if you haven't read 3263.
- Other RFC support : Wherever possible, resip provides code that implements all MUST and SHOULD-level requirements from supported RFCs (a lot of this code lives in resip::Helper).
The Big Picture
The basic anatomy of a SIP application running on top of the resip stack is as follows. You have:
These are all connected together through a combination of message-passing, and direct function calls (usually reserved for stuff like configuration).
Usage
- Initialize the logging system : See resip::Log::initialize() (in rutil) for details.
- Create a resip::SipStack : Fill in a resip::SipStackOptions with anything your application needs (for example, a resip::Security object for TLS, DTLS and S/MIME support) and pass it to the constructor.
- Add some transports to the stack : Use resip::SipStack::addTransport() to do this.
- Subclass resip::TransactionUser : The part of resip::TransactionUser that is left up to the app-writer is the code that reads messages out of the resip::TransactionUser's message-queue, and processes these messages. What the resip::TransactionUser does with these messages is very open-ended, but the stack expects that every request will get a final response.
- Register your resip::TransactionUser : This is done with resip::SipStack::registerTransactionUser().
- Give the stack processing cycles : Typically this is done with a resip::EventStackThread, which waits on the stack's resip::FdPollGrp and services it in a thread of its own; resip::EventStackSimpleMgr creates the poll group, the thread and the stack together. If you want to run your app and the SipStack in the same thread, call the stack's process(unsigned int timeoutMs) in your own loop instead. The older resip::StackThread and resip::InterruptableStackThread are deprecated.
- Optionally, call resip::SipStack::run() : This moves DNS, transaction processing and transport selection into threads of their own. The thread from the previous step is still needed, for application timers, statistics and shutdown.
- Shut down : Call resip::SipStack::shutdown() and keep processing until your TU receives a resip::ShutdownMessage, then stop your processing thread and call resip::SipStack::shutdownAndJoinThreads() if you used resip::SipStack::run().
The Internals
The resip stack can be broken down into the following large components:
Here's a description of the typical flow:
- resip::SipStack's main process loop runs, eventually causing one of the resip::Transport objects to read some bits off their fd. These bits are run through a pre-parse (tokenizes the message into header field values), and a basic validation (checks to see whether mandatory headers are present, and performs well-formedness checks on a very small number of headers). The resip::SipMessage is then posted to the resip::TransactionController's message queue.
- resip::TransactionController gets the resip::SipMessage, and creates a resip::TransactionState for it (supposing it is a new transaction). The transaction-state logic is carried out, until a determination is made on whether to send it to the resip::TransactionUser (app-layer).
- The resip::SipMessage is passed to the resip::TuSelector, which posts it to the appropriate resip::TransactionUser, using a chain-of-responsibility type pattern.
- The resip::TransactionUser gets the resip::SipMessage, and does any processing it needs to. If the message was a request, the app-layer will post a response to the resip::SipStack, which will place the message in the resip::TransactionController's message queue.
- resip::TransactionController will get the resip::SipMessage, perform any core transaction-state processing, and figure out where the message needs to go (back to the source; although if this were a request, it would look up the target asynchronously through resip::DnsStub). Once a target has been chosen, the resip::SipMessage is passed on to the resip::TransportSelector.
- resip::TransportSelector chooses what transport to send the message on, fills out some stuff in the resip::SipMessage, encodes it to a buffer, and posts it to the chosen resip::Transport.
- resip::Transport throws the bits out on the wire.