Introduction, background information
PBX is the short term for Private Branch eXchange. It is a system that connects telephone extensions of a company to outside public telephone network as well as to mobile networks. Today’s software-based IP PBXs can replace the traditional hardware business phone systems. PBXs work with VoIP technology, that is why an IP PBX provides audio/video calling and instant messaging communication through the TCP/IP protocol stack for its internal network and interconnects its internal network with the PSTN (Public Switched Telephone Network) for telephony communication.
As VoIP technology is evolving day by day, it is quite easy to boost your PBX with some additional functionalities, as well as dial plan that can be very useful in those communication networks where usually there are numerous simultaneous calls. A dial plan is the automated system in the server that manages the internal and external calls, the call forwarding, call hold and restrictions.
Due to these facts, I have implemented my own phone system based on SIP (Session Initiation Protocol) and I decided to share my project. Below I am going to show how to build a PBX then create some useful features to extend your communication system. (I especially recommend this article for beginners, since I have tried to explain the whole project step-by-step.)
Basic requirements for PBX development
For this development, as the program code has been written in C# programming language, you need an IDE (Integrated Development Environment) that supports C#. I prefer Microsoft Visual Studio. .NET Framework installed on your PC is also needed.
To define the default behaviour of the PBX I used the background support of Ozeki VoIP SIP SDK. This way my project requires Ozeki SDK installed on your PC as well.
For testing your phone system you will also need at least two IP telefones. For this purpose I used two softphones (X-Lite).
Creating a new Microsoft Visual Studio project
- After opening Visual Studio you need to create a new project by clicking on File then New project.
- As this PBX system will be a simple console application, in the next window you need to select the Visual C# Console Application option.
- After you have specified a name for your new project (such as MyPBX), click on the OK button.
- To achieve and use the PBX components of the SDK you need to add them to your references. (For this, you need to right-click on References than select the Add references option. After this, browse the SDK.dll file that can be found where the SDK has been installed to. Select the .dll and click on the OK button.)
Writing the code
Now you need to make a new class for your PBX. (Right-click on the project name, select add then choose the Class option. Provide a name for it – such as MyPBX – then click on the OK button.)
To use the SDK components and methods you need to add some using lines to the code:
using Ozeki.VoIP.PBX;
using Ozeki.VoIP.PBX.Extensions;
using Ozeki.VoIP.PBX.PhoneCalls;
using Ozeki.VoIP.SIP;
using Ozeki.VoIP.Network;
Code example 1: New using lines
When creating a PBX, your PBX class needs to be derived from the PBXBase
class provided by the SDK. Make a string type localAddress
variable and then the constructor. It has three parameters: localAddress
, minPortRange
, maxPortRange
. (Your PC may has more network addresses. The localAddress
is that address where the PBX listens the connecting clients. Besides the IP address, your PC has several other ports since each application use another port. You need to set the port range, indicated by the first paramerter as the minPortRange
and the maximum parameter maxPortRange
. This is the port's interval.) The localAddress
will be equal to the parameter localAddress. After that you need to put two lines to the console : PBX Starting…, What is our local address. Now you have a fully functional PBX that is able to register any SIP extensions and can establish phone lines in between them (Code example 2).
Now you need to create an OnStart method. First write the ’PBX started’ line to the console, then set up the listening port (check the following code example). The parameters: the localAddress
, the port number and the type of the transport (UDP). Write a new line: Listened port number. Call the base.OnStart
method. Finally, create the OnRegisterReceived
method that has 3 parameters (the extension, SIPaddress
, expires parameters) and the OnUnregisterReceived
method that has only 1 parameters (the extension). Please chect the Code example 2.
namespace myPBX
{
class MyPBX : PBXBase
{
string localAddress;
public MyPBX(string localAddress, int minPortRange, int maxPortRange) : base(minPortRange, maxPortRange){
this.localAddress = localAssress;
Console.WriteLine("PBX Starting...");
Console.WriteLine("Local address: " + localAddress);
}
protected override void OnStart()
{
Console.WriteLine("PBX started.");
SetListenPort(localAddress,5060,TransportType.Udp);
Console.WriteLine("Listened port: 5060(UDP)");
base.OnStart();
}
protected override RegisterResult OnRegisterReceived(ISIPExtension extension, SIPAddress from, int expires)
{
Console.WriteLine("Register received from: " + extension.Account.Username);
return base.OnRegisterReceived(extension, from, expires);
}
protected override void OnUnregisterReceived(ISIPExtension extension)
{
Console.WriteLine("Unregister received from: " + extension.Account.UserName);
base.OnUnregisterReceived(extension);
}
}
}
Code example 2: PBX class definition and the OnStart method
Now let’s make the main method for your PBX. For this, first you need to add a new using line to the code:
using Ozeki.Network;
Code example 3: One more new using line
Let’s see how to code the main method:
namespace myPBX
{
class Program
{
static void Main(string[] args)
{
var myPBX = new MyPBX(NetworkAddressHelper.GetLocalIP().ToString(), 20000, 20500);
myPBX.Start();
Console.ReadLine();
myPBX.Stop();
}
}
}
Code example 4: The main method
Let’s run the application. If it works, your PBX is ready to accept SIP account registrations then make and receive phone calls using the registered SIP extensions.
You can test SIP registration with your softphone. (You can use for instance X-Lite, or the demo softphone application that has been installed automatically with the SDK.) In case of using any softphone, after providing the required SIP account details, the register line will appear in your console application if the registration was successful (Figure 1):
Figure 1: Successful SIP account registration
To make a test call, you need to register two SIP accounts: Extension ’A’ and Extension ’B’. Having done the registration, you need to dial the phone number of Extension ’B’ by using Extension ’A’. The called party (Extension ’B’) will be ringing. After Extension ’B’ has accepted the call, the phone line is established between them and ready to make conversation.
Coding a dial plan
Below I intended to show how to define what should happen when somebody calls a phone number that is in DND status (Do Not Disturb). To create a dial plan first you need to make a new class. (Right-click on the project name, select add then choose the Class option. Provide a name for it – such as MyDialplanProvider – then click on the OK button.)
Add some using lines to the class:
using Ozeki.VoIP.PBX.Dialplan;
using Ozeki.VoIP.PBX.PhoneCalls;
using Ozeki.VoIP.PBX.Services;
Code example 5: Additional using lines required
Below you can see how to implement the IDialplanProvider
interface. Make a UserInfoContainer
, an IExtensionContainer
and a List type variables. The List will contain the dndExtensions
(dnd refers to Do Not Disturb). In the constructor you can make the basic settings, then add the IDialplanProvider
interface members (the Name, and the GetDestination
functions). (IDialplanProvider
can be used to control the destination of call forwarding. GetDestination
defines where the call will be forwarded to, and the routeParams
parameter contains such information related to the call as the called number, caller's phone number, etc.) After this, you need to create the dialedNumber
and callerPhoneNumber
variables and define some conditions according to the following code example:
namespace myPBX
{
class MyDialplanProvider : IDialplanProvider
{
UserInfoContainer userInfoContainer;
IExtensionContainer extensionContainer;
List <string> dndExtensions;
public MyDialplanProvider(UserInfoContainer userInfoContainer, IExtensionContainer extensionContainer)
{
dndExtensions = new List<string>();
this.extensionContainer = extensionContainer;
this.userInfoContainer = userInfoContainer;
}
public string Name
{
get { return "CustomDialplan"; }
}
public Destination GetDestination(RouteInfo routeParams)
{
var dialedNumber = routeParams.Destination.DialedNumber.UserName;
var callerPhoneNumber = routeParams.CallerInfo.Owner.Account.UserName;
if(dialedNumber == "*90" && !dndExtension.Contains(callerPhoneNumber))
{
Console.WriteLine("Extension {0} added to DND list", routeParams.CallerInfo.Owner);
<br /> dndExtensions.Add(callerPhoneNumber);
return null;
}
if(dialedNumber == "*91" && !dndExtension.Contains(callerPhoneNumber))
{
Console.WriteLine("Extension {0} removed to DND list", routeParams.CallerInfo.Owner);
<br /> dndExtensions.Remove(callerPhoneNumber);
return null;
}
if(dndExtension.Contains(callerPhoneNumber))
{
return null;
}
return new Destination(dialedNumber);
}
}
Code example 6: The implementation of the I
DialplanProvider
interface
Let’s go back to your PBX class and modify the OnStart()
method with the callManager
and extensionContainer
variables and with your DialplanProvider
:
protected override void OnStart()
{
Console.WriteLine("PBX started.");
SetListenPort(localAddress,5060,TransportType.Udp);
var callManager = GetService<ICallManager>();
var extensionContainer = GetService<IExtensionContainer>();
callManager.DialplanProvider = new MyDialplanProvider(userInfoContainer, extensionContainer);
Console.WriteLine("Listened port: 5060(UDP)");
base.OnStart();
}
Code example 7: Modifications in your PBX class
With two softphone you can easily test your system. If you dial number *90 from Extension 101, your phone number will be added to the DND list and it will impossible to call this user. But if you dial the *91, the number will be removed from the list and you can call it now (Figure 2).
Figure 2: How the dial plan functionality works
Creating voicemail functionality
Many companies can not afford to miss any incoming call. However, there are cases when it is inevitable. Voicemail service provides a practical solution for this problem as it allows your callers to leave a voice message for you.
Considering that the voicemail development is a bit wider topic, I though it would be better to describe my additional solution as a separate article. If you are interested in building voicemail feature, please study my tutorial that presents its implementation step-by step:
How to improve your VoIP PBX with voicemail functionality in C#:
http://www.codeproject.com/Articles/770111/How-to-improve-your-VoIP-PBX-with-voicemail-functi
Creating call queue functionality
Another possible solution to avoid any missed calls is call queuing. By using this feature, your PBX can manage the incoming calls in a queued system: if there are no any available call center agents, the incoming calls will be put at the end of the call queue. When an agent finishes the current call, the first call in the queue will be transferred to that agent.
Given the fact that the implementation of the call queue is a bit wider topic, I though it would be better to explain this improvement as a separate article. If you are interested in developing a virtual call queue extension, please study my step-by-step guide below:
How to develop a call queue in C# for improving your VoIP PBX:
http://www.codeproject.com/Articles/782342/How-to-develop-a-call-queue-in-Csharp-for-improvin
Summary
To sum it up, building your own SIP PBX can be pretty easy and fast if you use previously written components. After studying more SDKs, I found Ozeki’s VoIP SIP SDK really effective. It is a great tool if you want to focus on developing your application instead of worrying about necessary network protocols and technical details. Although, I have extended my PBX with only one additional feature (dial plan), it is possible to build several further functionalities of course. (For example, now I am working on a voicemail solution - in the meantime it has been completed, see above.)
Good luck!
References
Theoretical background:
Download required software: