By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,886 Members | 1,123 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

Building a virtual softphone in C# that works as a ring group in your call center

P: 1
This article focuses on the development of ring groups in C#. The effectiveness of any call center not only depends on the operators’ behaviour but also the technical background of the call center as well. After studying this guide you will be able to create a ring group extension (that is a „virtual softphone”) that can be used to transfer incoming calls to one of the selected call center agents based on a predefined ring group strategy.

Download Source Code:

  • Importance of the subject
  • A little bit more about the technical background of the call centers
  • Introduction to the ring group development
  • Development / Part 1: Building a softphone
  • Development / Part 2: Building additional features
  • Development / Part 3: Building the ring group with „one by one” strategy
  • Test
  • Conclusion
  • Further reading and references

Importance of the subject

A call centre or call center is a centralised office used to receive or transmit a large volume of requests by telephone [1]. In other words: „A call center is a physical place where customer and other telephone calls are handled by an organization, usually with some amount of computer automation” [2]. The „usually with some amount of computer automation” part of the previous definition indicates that a successful call center is based on the cooperation of human labor and certain kind of technical equipments. Consequently, the proper IT infrastructure is essential.

According to the experts, the careful selection of employees is the first important step towards a successful call center. But most of the skills can be learned. During first-line support , service desk and helpdesk trainings, the instructors often draw the operators’ attention to the followings:

  • Be kind and polite
  • Be proactive
  • Put first things first – let priorities drive initiatives
  • Think win-win – what’s good for your customer is good for you too, etc.

These are very important human factors that are essentially needed for a successful call center. In addition, certain management tools such as performance evaluation and regular feedback to employees, rewards, positive reinforcement, etc. are indispensable elements of an effective call center. But all human knowledge is futile in the absence of appropriate technology. It does not matter whether it is a small call center or a huge one, to gain more profit and build customer loyalty, the staff must use advanced hardware and software equipments.

What is common in an outsourced Indian call center and a huge American call center (Figure 1)? Yes, many things, but the most important one is the core technology. In both places, operators use VoIP desktop phones, softphones, headsets, what's more: advanced PBX systems with typical call center features (e.g. call forwarding, ring group, call recording, call queuing, conference calling, etc.) that makes certain tasks more easy due to the automation.

Figure 1: The technical equipments are key-factors in small and large call centers as well [3]

A little bit more about the technical background of the call centers

A call center has the ability to handle a huge amount of calls at the same time and to forward them to someone who is in charge of handling them. However, in today's accelerated world, VoIP technology, high-performance computers and smart software applications are almost mandatory. Today's call centers use more up-to-date equipment than those old fashioned huge „boxes” with lots of wires hanging out.

We can differentiate two main types of call centers as follows [1]:
  • Virtual call center: In this call center model, the operators pay usually a monthly or annual fee to a vendor that hosts the call center telephony equipment in their own data centre. Agents connect to the vendor’s equiment through traditional PSTN telephone lines, or via VoIP (Voice over Internet Protocol).
  • Premise-based call center: This model is the opposite of the previous one. In this case the call center has been built in a PBX (Private Branch eXchange) that is owned, hosted and maintained by the call centre operators themselves. This PBX provides advanced functionalities such as call queuing, ACD (Automated Call Distribution), IVR (Interactive Voice Response) or SBR (Skills-Based Routing – this is a call-assigment strategy that is used to assign incoming calls to the most suitable agent, instead of simply choosing the next available agent).

If you use a virtual call center, the features are fixed and you can not expand them indefinitely. But in case of premise-based call centers, you have the opportunity to improve your system by developing the required functionalities. Actually, only the imagination (and the corporate needs and conditions of course) limits this improvement.

Introduction to the ring group development

In this article you will see an example of the latter case. To be precise, this guide demonstrates step-by-step the creation of a ring group. This ring group will be a „virtual” extension installed in your PBX. This extension is based on a softphone (that is – consequently – registered to the PBX). The PBX assigns a telephone number to this extension. When it is called, the extension tries to transfer the incoming call to one of the other extensions (that is to one of the members of the ring group that are has been registered previously to the PBX). The ring group strategy determines that which member will accept the call.

I am afraid, the previous thread contains some new (or scary) expressions… whether so, or not, let me clarify the basic elements of this solution for the sake of completeness.
  • What is ring group: In the modern age of telecommunication call routing allows more than one extension to ring when there is an incoming call. It is called ring group. The ring group usually appears as a „virtual extension” in a PBX. During the configuration of the ring group, it can be defined, which telephone extensions belong to the group. This way, when there is an incoming call to the „virtual extension”, all the belonging extensions will ring simultaneously or sequentially within the ring group.
  • What does it mean „extension”: In business telephony, a telephone extension may refer to a phone within the internal telephone network attached to a PBX. Within the PBX, the users only need to dial the extension number to reach any other user directly. For inbound calls, after dialing the corporate phone number, a switchboard operator or automated attendant may request the number of the preferred extension . The call can be also completed by direct inbound dialing if outside numbers are assigned to individual extensions [5].
  • What is softphone: Softphones are a special kind of computer software, functioning as a virtual telephone, allowing people to communicate with others over the Internet using their PC or laptop.
  • What is call transferring: Call transferring is a telecommunication mechanism allowing the user to relocate an existing phone call to another telephone or attendant console, by using a transfer button or a switchhook flash and dialing the required location [6].
  • What is ring group strategy: The ring group strategy determines the way how the group members will be notified if there is an incoming call. Before call transferring, the ring group needs to notify the group members that call transferring is needed. To select the member who will accept the call within the ring group, several strategies can be used. Let’s see some examples: It can be configured that all extensions (that is all members’ telephone) in the ring group will be called simultenaously. It can be also implemented that the ring group extension will call the members one by one in a previously set order. An other commonly used strategy is the case when the extensions of the ring group will be called in a randomized order. (In this project the „one by one” strategy will be demonstrated.)

Based on the above, let’s summarize what developments are needed for this project:
  • Building a virtual (or console) softphone that is able to receive phone calls
  • Building the required additional features such as call transfer and multiple call management
  • Building the ring group with „one by one” strategy

Accordingly, I used the following 3 classes:
  1. Softphone.cs: It presents how to develop a softphone using C# that can be registered to your PBX.
  2. Program.cs: This class is responsible for handling the user events. It presents how to register the softphone to your PBX.
  3. RingGroupCallHandler.cs: This class illustrates how to create the necessary ring group strategy. Due to this separated class, the application can handle multiple incoming calls at the same time using its instances.

First and foremost, you will need a new Visual C# Console Application, since this application is actually a „virtual softphone”, so a console application is just enough. For better understanding I divided the rest of this article into 3 main parts. All the three sections present the implementation of one class step-by-step.

Now, that the theoretical and development background is exhausted, „keep calm and start programming!” :)

Development / Part 1: Building a softphone

Are you ready? Sure! :) Well, let’s start by the Softphone.cs class. As a first step, add the required using lines as follows. (As a C#.NET VoIP library, Ozeki VoIP SIP SDK has been used - Please keep in mind that this code has been written by using the prewritten VoIP components of this SDK. So if you want to try them out, you need to install its freely available trial version. [7])

Expand|Select|Wrap|Line Numbers
  1. using System;
  2. using Ozeki.VoIP;
  3. using Ozeki.VoIP.SDK;
Now there is a need for a softphone and a phone line object. You can get them from the ISoftPhone and the IPhoneLine interfaces as follows:

Expand|Select|Wrap|Line Numbers
  1. ISoftPhone _softphone;
  2. IPhoneLine _phoneLine;
In a constructor, you need to initialize this softphone by using default parameters. The 5000 and 10000 parameters refer to the port range: the first number is the minimum port’s number (minPortRange) and the other one is the maximum port’s number (maxPortRange). To achieve that incoming calls will be monitored continuously, you need to subscribe to the IncomingCall event. Check out the above-described steps below:

Expand|Select|Wrap|Line Numbers
  1. public Softphone()
  2.         {
  3.             _softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
  4.             _softphone.IncomingCall += softphone_IncomingCall;
  5.         }
By subscribing to the IncomigCall event, you will be notified if there is an incoming call. And the PhoneLineStateChange event notifies you (after subscribing of course) if the state of the phone line changes. Check out the above-described steps below:

Expand|Select|Wrap|Line Numbers
  1. public event EventHandler<VoIPEventArgs<IPhoneCall>> IncomigCall;
  2. public event EventHandler<RegistrationStateChangedArgs> PhoneLineStateChanged;

By using the Register method, you can register the softphone to the PBX. For this purpose a phone line should be created that requires a SIP account. To be able to create a SIP account for your softphone, you need to specify the following parameters:
  • registrationRequired: To be able to receive incoming call, you need to set „true” value for this parameter.
  • displayName: This is a name to be displayed at the called client.
  • userName: This is a number to be dialled by an other client if that want to call this softphone.
  • authenticationId: This is an identifier for the PBX (like a login name).
  • registerPassword: This is the password belonging to the authenticationId that is used to register to the PBX.
  • domainHost: This is a domain name, that is the IP address of the PBX you want to register to.
  • domainPort: This is the port number of the PBX you want to register to.

After creating the necessary SIP account, you can create the phone line to be able to communicate with the PBX. It is also required to listen to the changes of the phone line’s state. And finally, the RegisterPhoneLine method can be used to register the phone line to the softphone.

Check out the above-described steps below:

Expand|Select|Wrap|Line Numbers
  1. public void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost, int domainPort)
  2.         {
  3.             try
  4.             {
  5.                 var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
  6.                 Console.WriteLine("\nCreating SIP account {0}", account);
  8.                 _phoneLine = _softphone.CreatePhoneLine(account);
  9.                 Console.WriteLine("Phoneline created.");
  11.                 _phoneLine.RegistrationStateChanged += _phoneLine_RegistrationStateChanged;
  13.                 _softphone.RegisterPhoneLine(_phoneLine);
  14.             }
  15.             catch (Exception ex)
  16.             {
  17.                 Console.WriteLine("Error during SIP registration" + ex.ToString());
  18.             }
  19.         }
This will be called when the registration state of the phone line has changed:

Expand|Select|Wrap|Line Numbers
  1. void _phoneLine_RegistrationStateChanged(object sender, RegistrationStateChangedArgs e)
  2. {
  3.     var handler = PhoneLineStateChanged;
  4.     if (handler != null)
  5.         handler(this, e);
  6. }
And this will be called when there is an incoming call:

Expand|Select|Wrap|Line Numbers
  1. void softphone_IncomingCall(object sender, VoIPEventArgs<IPhoneCall> e)
  2. {
  3.     var handler = IncomigCall;
  4.     if (handler != null)
  5.         handler(this, e);
  6. }
The following code snippet can be used to create a call object, when it is called:

Expand|Select|Wrap|Line Numbers
  1. public IPhoneCall CreateCall(string member)
  2. {
  3.     return _softphone.CreateCallObject(_phoneLine, member);
  4. }
This is the end of the implementation of the Softphone.cs. I understand if you want to have a rest right now. Take a break and let’s continue by coding the Program.cs! :)

Development / Part 2: Building additional features

Having done the creation of the softphone, let’s continue the programming with the Program.cs. This class presents the usage of the softphone object, handles the console events, interacts with the user, and uses the opportunities provided by the Softphone class. (You will see that everything are handled in separated methods that communicates with each other.)

Below the Main method can be seen where you need to initialize the softphone by calling an initializer method. (The BlockExit method at the end of the code snippet - it don't let the application to exit.)

Expand|Select|Wrap|Line Numbers
  1. static List<RingGroupCallHandler> _callHandlers;
  2. static List<String> _members;
  3. static int _memberCount;
  4. static Softphone _softphone;
  6. static void Main(string[] args)
  7. {
  8.     _softphone = new Softphone();
  10.     ShowHelp();
  12.     SipAccountInitialization(_softphone);
  14.     _callHandlers = new List<RingGroupCallHandler>();
  18.     _softphone.IncomigCall += softphone_IncomigCall;
  19.     _softphone.PhoneLineStateChanged += softphone_PhoneLineStateChanged;
  22.     BlockExit();
  23. }
Now, there is a new softphone that monitors the states of phone line. You have subscribed to get notified if the state of the phone line has been changed. Below you can see that the Softphone_PhoneLineStateChanged method determines what to do for each state. This will be called when the registration state of the phone line has changed:

Expand|Select|Wrap|Line Numbers
  1. static void softphone_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
  2. {
  3.     Console.WriteLine("Phone line state changed to: {0}", e.State);
  5.     if (e.State == RegState.RegistrationSucceeded)
  6.         MemberAdder();
  7. }
Concerning to the fact that the class is also subscribed to the phone line's events, it is notified when the phone line's state is successfully registered, and it starts to ask the user about the ring group: number of the members, their phone numbers. These are being stored in a list of Strings:

Expand|Select|Wrap|Line Numbers
  1. private static void MemberAdder()
  2.         {
  3.             _members = new List<String>();
  5.             while (_memberCount == 0 || _memberCount == null)
  6.             {
  7.                 Console.Write("\nThe number of the RingGroup members: ");
  8.                 try
  9.                 {
  10.                     _memberCount = Int32.Parse(Console.ReadLine());
  11.                 }
  12.                 catch
  13.                 {
  14.                     Console.WriteLine("Wrong input!");
  15.                 }
  16.             }
  18.             for (int i = 0; i < _memberCount; i++)
  19.             {
  20.                 Console.Write("{0}. member's phone number: ", i + 1);
  21.                 string member = Console.ReadLine();
  22.                 _members.Add(member);
  23.             }
  25.             Console.WriteLine("\nWaiting for incoming calls...");
  26.         }
As the Program.cs communicates with the user through a console window; it is worth to provide a short introduction message for the user:

Expand|Select|Wrap|Line Numbers
  1. static void ShowHelp()
  2.         {
  3.             Console.WriteLine("Hello! This is a brief introduction to this project.");
  4.             Console.WriteLine("This project is about to introduce the creation of a RingGroup (with the one by one strategy), which works on the following way:");
  5.             Console.WriteLine("1., the application asks the user about the number of the RingGroup members");
  6.             Console.WriteLine("2., also asks the user, to enter the members' phone numbers");
  7.             Console.WriteLine("3., waits for an incoming call");
  8.             Console.WriteLine("4., starts to ring the first RingGroup on the list of members");
  9.             Console.WriteLine("5., if the member is busy or cannot be reached, it starts to ring an other member");
  10.             Console.WriteLine("6., if the call is being answered, it transfers the call and stops ringing the other members");
  11.             Console.WriteLine("-------------------------------------------------------------------------------");
  12.             Console.WriteLine();
  13.         }
If there is an incoming call, the user will be notified and the application creates a new RingGroupCallHandler object for the call, subscribes to its Completed method, then adds the object to the List of the handlers. Thereafter it calls the object's Start method:

Expand|Select|Wrap|Line Numbers
  1. static void softphone_IncomigCall(object sender, VoIPEventArgs<IPhoneCall> e)
  2.         {
  3.             Console.WriteLine("\nIncoming call from \"{0}\"!\n", e.Item.DialInfo.Dialed);
  5.             var callHandler = new RingGroupCallHandler(e.Item, _softphone, _members);
  6.             callHandler.Completed += callHandler_Completed;
  8.             lock (_callHandlers)
  9.                 _callHandlers.Add(callHandler);
  11.             callHandler.Start();
  12.         }
When the incoming call is no longer available – e.g. because it was transferred or the caller finished the call etc. – the RingGroupCallHandler object should be removed from the List of the call handlers:

Expand|Select|Wrap|Line Numbers
  1. static void callHandler_Completed(object sender, EventArgs e)
  2.         {
  3.             lock (_callHandlers)
  4.                 _callHandlers.Remove((RingGroupCallHandler)sender);
  5.         }
To be able to use the previously created softphone for communication, a SIP account needs to be set. For this reason, the application should ask the user to enter valid SIP account details and try to register to the PBX. For detailed explanation please check out the comments in the following code shippet:

Expand|Select|Wrap|Line Numbers
  1. static void SipAccountInitialization(Softphone softphone)
  2.         {
  4.             var registrationRequired = true;
  5.             Console.WriteLine("\nPlease set up Your SIP account:\n");
  7.             // Asks, if a registration is required to the PBX. The default value is true.
  8.             Console.Write("Please set if the registration is required (true/false) (default: true): ");
  9.             var regRequired = Read("Registration required", false);
  10.             if (regRequired.ToLower() == "false" || regRequired.ToLower() == "no" ||
  11.                  regRequired.ToLower() == "n")
  12.             {
  13.                 registrationRequired = false;
  14.             }
  15.             else
  16.             {
  17.                 Console.WriteLine("Registration set to required.");
  18.             }
  21.             // The SIP account needs and authentication ID, and some names as well.
  22.             Console.Write("Please set Your authentication ID: ");
  23.             var authenticationId = Read("Authentication ID", true);
  25.             // If the user only presses the Enter button, the username will be the same as the authentication ID
  26.             Console.Write("Please set Your username (default: " + authenticationId + "): ");
  27.             var userName = Read("Username", false);
  28.             if (string.IsNullOrEmpty(userName))
  29.                 userName = authenticationId;
  31.             // If the user only presses the Enter button, the display name will be the same as the authentication ID
  32.             Console.Write("Please set Your name to be displayed (default: " + authenticationId + "): ");
  33.             var displayName = Read("Display name", false);
  34.             if (string.IsNullOrEmpty(displayName))
  35.                 displayName = authenticationId;
  37.             // The registration password needs to be entered.
  38.             Console.Write("Please set Your registration password: ");
  39.             var registerPassword = Read("Password", true);
  41.             // Domain name as a string, for example an IP adress.
  42.             Console.Write("Please set the domain name: ");
  43.             var domainHost = Read("Domain name", true);
  45.             // Port number with the as 5060 default value.
  46.             Console.Write("Please set the port number (default: 5060): ");
  47.             int domainPort;
  48.             string port = Read("Port", false);
  49.             if (string.IsNullOrEmpty(port))
  50.             {
  51.                 domainPort = 5060;
  52.             }
  53.             else
  54.             {
  55.                 domainPort = Int32.Parse(port);
  56.             }
  59.             Console.WriteLine("\nCreating SIP account and trying to register...\n");
  60.             softphone.Register(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
  62.         }
After this there is a need for a helper method to read the inputs:

Expand|Select|Wrap|Line Numbers
  1. private static string Read(string inputName, bool readWhileEmpty)
  2.         {
  3.             while (true)
  4.             {
  5.                 string input = Console.ReadLine();
  7.                 if (!readWhileEmpty)
  8.                 {
  9.                     return input;
  10.                 }
  12.                 if (!string.IsNullOrEmpty(input))
  13.                 {
  14.                     return input;
  15.                 }
  17.                 Console.WriteLine(inputName + " cannot be empty!");
  18.                 Console.WriteLine(inputName + ": ");
  19.             }
  20.         }
And finally, you need to use the BlockExit method. This will not let the application to exit:

Expand|Select|Wrap|Line Numbers
  1. private static void BlockExit()
  2.         {
  3.             while (true)
  4.             {
  5.                 Thread.Sleep(10);
  6.             }
  7.         }
Yay, now we are ready with Softphone.cs and Program.cs as well. If it is needed, take a short break before getting started the last major section! :)

Development / Part 3: Building the ring group with „one by one” strategy

Now comes the last major section that demonstrates the implementation of the RingGroupCallHandler class. This class is responsible for waiting for incoming calls, starting to call the member of the ring group based on a preferred ring group strategy, and transferring the call to the appropriate member.

The Start method should be called when there is an incoming call. The Start method accepts the call, then starts to notify the ring group members by calling the StartOutgoingCalls method.

Expand|Select|Wrap|Line Numbers
  1. IPhoneCall _incomingCall;
  2. Softphone _softPhone;
  3. List<IPhoneCall> _calls;
  4. ist<String> _members;
  6. object _sync;
  8. public RingGroupCallHandler(IPhoneCall incomingCall, Softphone softPhone, List<String> members)
  9. {
  10.     _sync = new object();
  11.     _incomingCall = incomingCall;
  12.     _softPhone = softPhone;
  13.     _members = members;
  15.     _calls = new List<IPhoneCall>();
  16. }
  18. public event EventHandler<VoIPEventArgs<CallError>> CallErrorOccured;
  19. public event EventHandler Completed;
  21. public void Start()
  22. {
  23.     _incomingCall.Answer();
  24.     _incomingCall.CallStateChanged += call_CallStateChanged;
  25.     StartOutgoingCalls();
  26. }
The StartOutgoingCalls method creates call objects to each member and the CallSequencer method will be called. It is needed to subscribe to the CallStateChanged event. When the event occurs, the application will call the OutgoingCallStateChanged method. (It will be used when an incoming call is answered, and it should be transferred to an agent who is in the ring group.)

Expand|Select|Wrap|Line Numbers
  1. void StartOutgoingCalls()
  2.         {
  3.             foreach (var member in _members)
  4.             {
  5.                 var call = _softPhone.CreateCall(member);
  6.                 call.CallStateChanged += OutgoingCallStateChanged;
  7.                 _calls.Add(call);
  8.             }
  10.             CallSequencer();
  11.         }
If there are calls in the call list, the CallSequencer method rings the members one by one (starting by the first member on the list). If there are not any call in the call list, the method hangs up the incoming call:

Expand|Select|Wrap|Line Numbers
  1. private void CallSequencer()
  2.         {
  3.             if (_calls.Count > 0)
  4.             {
  5.                 var call = _calls[0];
  6.                 Console.WriteLine("Ringing phone number \"{0}\"", call.DialInfo.Dialed);
  7.                 call.Start();
  8.             }
  9.             else
  10.             {
  11.                 Console.WriteLine("No available RingGroup member has been found.");
  12.                 _incomingCall.HangUp();
  13.             }
  14.         }
The OutgoingCallStateChanged method transfers that caller (that is the incoming call) to one of the ring group members by using the AttendedTransfer method. The OutgoingCallStateChanged method is also responsible for removing the call from the call list, then calling the OnCompleted method. When the called is busy or cannot be reached, the application will call the CallSequencer method again.

Expand|Select|Wrap|Line Numbers
  1. void OutgoingCallStateChanged(object sender, CallStateChangedArgs e)
  2.         {
  3.             var call = (IPhoneCall)sender;
  5.             if (e.State == CallState.Answered)
  6.             {
  7.                 Console.WriteLine("\nCall has been accepted by {0}.", call.DialInfo.Dialed);
  8.                 Console.WriteLine("Call from \"{0}\" is being transferred to \"{1}\".\n", _incomingCall.DialInfo.Dialed, call.DialInfo.Dialed);
  9.                 _incomingCall.AttendedTransfer(call);
  11.                 lock (_sync)
  12.                 {
  13.                     _calls.Remove(call);
  14.                     OnCompleted();
  15.                 }
  16.             }
  18.             if (e.State == CallState.Busy || e.State == CallState.Error)
  19.             {
  20.                 lock (_sync)
  21.                 {
  22.                     if (call != null)
  23.                     {
  24.                         Console.WriteLine("Ringing phone number \"{0}\" ends.", call.DialInfo.Dialed);
  25.                         call.HangUp();
  27.                         _calls.Remove(call);
  28.                         CallSequencer();
  29.                     }
  30.                 }
  31.             }
  32.         }
When a call is answered and transferred to a ring group member, the OnCompleted method will be called. The OnCompleted method calls the HangupOutgoingCalls method, and sets the Completed event. This indicates that the call transferring was successful, or the caller party has hanged up the call before transferring. The Completed event also informs the the application, that the call handler has no more to do. (The OnCompleted method is also called, when the incoming call has finished before transferring.)

Expand|Select|Wrap|Line Numbers
  1. void call_CallStateChanged(object sender, CallStateChangedArgs e)
  2. {
  3.     if (e.State.IsCallEnded())
  4.     {
  5.         OnCompleted();
  6.     }
  7. }
  9. void OnCompleted()
  10. {
  11.     HangupOutgoingCalls();
  13.     var handler = Completed;
  14.     if (handler != null)
  15.         handler(this, EventArgs.Empty);
  16. }
The HangupOutgoingCalls method is used to hang up all outgoing calls and to clear them from the list:

Expand|Select|Wrap|Line Numbers
  1. void HangupOutgoingCalls()
  2. {
  3.     foreach (var call in _calls)
  4.     {
  5.         call.HangUp();
  6.     }
  7.     _calls.Clear();
  8. }
  10. void call_CallErrorOccured(object sender, VoIPEventArgs<CallError> e)
  11. {
  12.     Console.WriteLine("Error occured at {0}: {1}", ((IPhoneCall)sender).DialInfo.Dialed, e.Item);
  14.     var handler = CallErrorOccured;
  15.     if (handler != null)
  16.         handler(this, e);
  17. }
And this is the end of the development! Congratulation for your new application! Your job is almost over. Now you only need to test the ring group by using your new virtual softphone.


Let’s run the application to test the ring group functionality of your new virtual softphone. Please note that some SIP accounts (whether they are desktop VoIP phones, softphones or mobile extensions) should be installed previously in your PBX to be able to add members to the ring group. For instance, I have registered three SIP accounts (1000, 2000, 3000) in my PBX. (Two will be a ring group member, and I use the third one to make a test call.)
After pressing F5, the previously written introduction can be seen in your console application. After the introduction, the application asks you to set up your SIP account (Figure 2).

Figure 2: The ring group as a console application after running it

Now you need to configure your virtual softphone and the ring group itself. First of all, set registration to required by entering „true” (Figure 3). After this, you need to create a new SIP account in your PBX that will be used as a virtual softphone extension for ring group purposes (in my case this is the „4000” numbered SIP account). Now provide the same SIP account details within your new console application by entering the required data. The domain name is the IP address of your PBX and the port number is its port number (that usually is 5060). After providing the necessary SIP account details, the application tries to register to the PBX. If the information provided is correct, the phone line state changes to RegistrationSucceeded. Thereafter, the application asks you to enter the number of the ring group members and to define a sequence. This order determines that which SIP account (that is which phone) will be ringing when someone dials the telephone number of the ring group. (The application will forward the incoming call to the first member. If that is unavailable, the call will be forwarded to the second member, etc.) Now the configuration is completed, the application is waiting for an incoming call (Figure 3).

Figure 3: The ring group console application after running it

My test call can be seen below (Figure 4). I used „3000” to initiate a phone call. I dialled „4000” (that is the telephone number of the ring group – it can be function as a central phone number of a company). After this, the first ring group member, the „2000” numbered extension started to ring. However, this extension was busy, therefore the second member, „1000” started to ring. This extension accepted the call, so the incoming call has been transferred from „4000” (virtual softphone) to „1000” (desktop VoIP phone).

Figure 4: Test call

It means that your application works well! Congratulation for your success! :)


In todays accelerated world, companies need up-to-date software and hardware devices as well in all areas of business – so during the communication with customers and other partners, too. Call centers should be equipped with effective solutions that helps in automation. Call forwarding, ring group, call recording, call queuing, conference calling, etc. are just some of the great features that can be achieved if you use VoIP technology (and an IP PBX). In this tutorial I wanted to show through the example of ring groups how easy can be to develop a missing functionality on your own in order to improve your call center. I hope you liked it!

Further reading and references

To compose this C# tutorial on building a ring group I used the following knowledge base:
Jan 22 '15 #1
Share this Article
Share on Google+