High performance VoIP SDK for .Net developers

VoIP SIP SDK

Deprecated Content

Source code explanation for building a C# SIP softphone client that can register with a SIP service provider

This page provides information about how to build a C# SIP softphone client that can register with a SIP service provider.

Ozeki C# SIP softphone client example can be obtained by
clicking the download link below:
Download Ozeki C# SIP softphone client example!

The communication devices of SIP – SIP messages

SIP messages are text messages defining UTF-8 encoded requests and responses in client-server communication. Both message types are based on the same form. Each SIP messages consist of a start-line and one or more message headers, then one empty line (CRLF) that is followed by the optional message body. CLRF divides the message header from the message body. CLRF line is mandatory even in cases when there is no message body in the SIP message.

generic-message = start-line
*message-headers
CRLF
  [message-body]
start-line = Request-Line / Status-Line

It can be obviously decided if the message is a request or a response on the basis of its start-line. The start-line is either a request-line or a status-line.

The structure of request-line

Request-Line = Method SP Request-URI SP SIP-Version CRLF

- where SP is a single space character, CLRF is carriage-return line-feed
- where Method defines the method to be executed. A simple call can be handled with the following methods: REGISTER, INVITE, ACK, BYE and CANCEL
- where Request-URL is a SIP address
- where SIP-version is the version number of the used SIP

The structure of status line

Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF

- where Status-Code is a 3 digit „integer” number that identifies in which of the six response groups the given response message belongs (informative responses, successful connection responses, redirect, unsuccessful request, server errors, generic errors)
- where Reason Pharse is the text name that is related to status code

Message header

SIP protocol defines several message-header types. The information content of these message-header types and the syntax of providing this information mainly depend on the message-header type itself. Moreover the semantic interpretation of the same header-fields may also differ from the role the SIP message has in its environment. Considering the length of this article I will not deal more with the detailed syntax and semantics analysis regarding the header field types. I will only mention the ones that are needed to ring a telephone (via, to, from, call-id, CSeq, Contact, Content-Length).

The general form requirements of SIP header fields:

field-name: field-value

The requirements regarding the syntax and semantics of field-value can strongly differ according to the header type. For more detailed information please have a look at the reference of SIP RFC3264.

Registration, authentication

Registration can take place in the communication process between an UA and a server. The registration process can also accompany with an authentication process if the server requires it. Besides registration some scenarios and events can make authentication necessary for UAC such as initializing a call, terminating a call via BYE SIP message or modification of a session.

SIP authentication is based on HTTP authentication and uses the „DIGEST” mechanism: the registration of UA to a SIP PBX is made by sending a registration request to the server. The server returns the authentication information needed for registration to the UA that wants to register.

This time the UA restructures the same registration message and completes it with an authentication header. The authentication header is created from the information received from the server. Now let's see it with a concrete example!

So the UA sends a REGISTER SIP message to the SIP server, in this case, as a result of a registration request (it could be a call initialization because authentication is also required in that case).

string message = "REGISTER sip:192.168.91.212:5060 SIP/2.0\r\n"
                + "Via:SIP/2.0/UDP "+mylocalIp+":5701;branch=z9hG4bK5b5077e0-e26f-4a50-bf16-10b5b22b591c;rport\r\n"
                + "To: " + myUserName + "<sip:" + myUserName + "@" + SIPproviderIP + ":5060>\r\n"
                + "From: " + myUserName + "<sip:" + myUserName + "@" + SIPproviderIP + ":5060>;tag=hetfgeeb\r\n"
                + "Call-ID: gdhisemewofqcjxysiwjjnrtfdwsfcmykvspmdxfnpllcrdsyi\r\n"
                + "CSeq: 1 REGISTER\r\n"
                + "Contact: <sip:" + myUserName + "@" + mylocalIp + ":5780>\r\n"
                + "Expires: 3600\r\n"
                + "Supported: 100rel\r\n"
                + "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE\r\n"
                + "Max-Forwards: 70\r\n"
                + "Content-Length: 0\r\n"
                + "\r\n";

The structure of a Register message

The formal requirements of SIP messages, that were mentioned above, can be identified here. The following line compiles the request line of the message and it clearly shows that it is a SIP request.

"REGISTER sip:192.168.91.212:5060 SIP/2.0\r\n"

The following line marks the SIP server address (SIP uri) to which I would like to register. It is followed by the version number SIP/2.0\r\n" of the used SIP that is followed by the Carriage Return characters and they also close the start-line of the SIP message.

sip:192.168.91.212:5060 

Via header

"Via:SIP/2.0/UDP "+mylocalIp+":5701;branch=z9hG4bK5b5077e0-e26f-4a50-bf16-10b5b22b591c;rport\r\n"

This line is already a header field that is mandatory to create a request. It marks the used SIP version of which value needs to be constant SIP/2.0 according to the current SIP standard. It also marks the data transfer protocol and the address where the addressee device needs to send its response to. Furthermore, a ; divided branch parameter that identifies the transaction created by the request definitely. The transaction has not been mentioned yet, it is not really important for our example but it is worth mentioning a few words about it.

SIP transactions

The SIP protocol organizes its messages into collections that includes a request and all the responses related to this request. These collections are called SIP transactions. Transactions are created with requests and they are ended with one or more final responses depending on the given request and its environment. More details about the semantics can be found in RFC3261 reference. So all requests create a transaction with the identifier of the VIA header branch parameter. With the help of it, the participants of the communication can definitely identify that the incoming SIP messages which of the transactions are related to. The branch parameter is built up by two parts. One part is "z9hG4bK" that is a constant value according to the standard. The other part of the branch parameter is a string that has been generated by a random generator.

To header

To: " + myUserName + "<sip:" + myUserName + "@" + SIPproviderIP + ":5060>\r\n"

It includes the recipient of the request message and its first optional parameter is the display name of the caller. It is the myUserName in this example. This is followed by the SIP URI of the recipient that can be followed by the TAG parameter. TAG is another definition to identify dialogues; it is a string that is created randomly.

Dialogue

The connection evolved between two or more SIP devices during the call is called dialogue. Usually dialogues are created with an INVITE message and destroyed with the BYE message. The call-id is the TAG parameter of „to” header and „from” header and it identifies a dialogue.

From header

"From: " + myUserName + "<sip:" + myUserName + "@" + SIPproviderIP + ":5060>;tag=hetfgeeb\r\n"

This message header includes the optional display name that creates a request as well as its SIP uri. Also it contains the randomly generated string TAG which identifies the current dialogue.

Call-ID header

"Call-ID: gdhisemewofqcjxysiwjjnrtfdwsfcmykvspmdxfnpllcrdsyi\r\n" 

It is a generated unique identifier and its value is the same within a dialogue in every request and response message. It is meant to identify the dialogues that are separated from each other.

CSeq header

"CSeq: 1 REGISTER\r\n"

This is the header that identifies the transactions between SIP devices through an unsigned 32 bit integer number. This number is ascending by +1 in the consecutive transactions. Also the header includes the method that creates the transaction in this case it is the REGISTER.

Contact header

"Contact: <sip:" + calleeUserName + "@" + calleeIPAddress + ">\r\n"

The contact header includes the UA SIP address that creates the request. Via this header the UAs participating in the communication can contact with the given UA.

Expires header

"Expires: 3600\r\n"

It specifies that the given UA registration should be active in what time period; it is expressed in seconds. When this time is up, a new registration is needed for the UA to receive calls.

Supported header

"Supported: 100rel\r\n"

It marks the optional SIP extensions of the UAC for the server.

Allow header

"Allow: INVITE, ACK, CANCEL, OPTIONS, BYE\r\n"

It includes the list of methods that can be processed by the given UA.

MAX forwards header

"Max-Forwards: 70\r\n"

All request messages include this header. It defines with the help of an integer number that how many hops are needed to the given message to reach its destination. The standard suggest 70 value to identify this value.

Content-Length

"Content-Length: 0\r\n"

The content-length header includes the length of the SIP message body and it is in bytes. In this sample code this length is 0 because the aim of the sample is only to ring a telephone.

Responses given to registration messages

To this SIP message the SIP server will respond with a 401 Unauthorized response message in case the UAS is a registrar or a redirect server. If the server is a proxy server then a 407 Proxy Authentication Required message is returned that includes the information which is required for the authentication in the WWW-Authenticate header:

SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.91.42:5701;branch=z9hG4bK5b5077e0-e26f-4a50-bf16-10b5b22b591c;received=192.168.91.42;rport=56498
From: oz871<sip:oz871@192.168.91.212:5060>;tag=hetfgeeb
To: oz871<sip:oz871@192.168.91.212:5060>;tag=as2fa69ea8
Call-ID: gdhisemewofqcjxysiwjjnrtfdwsfcmykvspmdxfnpllcrdsyi
CSeq: 1 REGISTER
Server: Asterisk PBX 1.6.2.9-2ubuntu2
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO
Supported: replaces, timer
WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="64b5309c"
Content-Length: 0

WWW-Authenticate header

Digest: It marks the name of the authentication system.
algorithm: It marks the algorithm that is used to calculate checksum. By default it is MD5.
realm: Associated protection domain.
nonce: A unique string that is defined by the server.

Registration completed with authentication header field

To finish registration successfully you need to resend your request by completing it with the Authorization header.

Authorization: Digest username=oz871, realm="asterisk", nonce="64b5309c", response="7d612fdbb2b83b21cd6727faf4fcd014", uri="sip:192.168.91.212:5060", algorithm=MD5 

Where Digest is the name of the authentication mechanism, the username is the defined name on the used realm, in this case it is „asterisk”. Nonce is the string that we previously received in the 401 or 407 response message, the response field is the key that is calculated on the basis of the signed MD5 algorithm. This is done with the utilization of username, realm, userpassword, sip method, sip uri and nonce. With it the server does the identification. In case the calculated value is correct you receive a 200 OK response message that informs you about the successful registration. In case the value is incorrect, 401 Unauthorized, 407 Proxy Authentication Required messages will be returned.

Transmission layer

SIP makes communication through UDP (User Datagram Protocol) protocol. It can be realized easily with the help of Microsoft .NET Framework. In this sample program I do the sending and receiving of messages via the UdpClient object by using only a few lines:

UdpClient udpClient = new UdpClient();
  udpClient.Connect(SIPproviderIP, 5060);
                Byte[] sendBytes = Encoding.ASCII.GetBytes(sipMessage);
                udpClient.Send(sendBytes, sendBytes.Length);
                Byte[] receiveBytes = udpClient.Receive(ref remotendpoint);
                string receivedMessage = Encoding.ASCII.GetString(receiveBytes);

Ozeki C# SIP softphone client example can be obtained by
clicking the download link below:
Download Ozeki C# SIP softphone client example!

If you have any further questions do not hesitate to contact us! Send an Email to info@voip-sip-sdk.com