The Inetlab SMPP library implements SMPP protocol for two-way SMS messaging over TCP/IP. It allows to communicate with the SMSC (Short Message Service Center) or SMS provider. Using the library, you can send SMS messages to customers, receive messages from mobile devices and process delivery receipts. It supports long text messages in any encoding.
This is a robust SMPP framework for building production-grade solutions. Inetlab SMPP is helpful in such tasks as:
notifying users
command receiving from mobile subscribers (i.e. accounts balance requests)
creation of SMS Gateway for SMS traffic reselling
and many other applications.
The Inetlab SMPP library is fully compliant with SMPP specifications v3.3, v3.4, v5.0 and comes with a comprehensive set of code samples. Enjoy exploring our demo applications, knowledge base and best support from our development team. Inetlab developers will review your code and even analyze your Wirshark network SMPP data logs!
Features
SMPP Client
SMPP client integrates SMS functionality into your application. It serves for connecting to an SMSC over TCP/IP, sending and receiving short messages from the SMSC. The class implements events fired on receiving messages from mobile network. It also provides tools for programming interactions with any USSD center.
Features
Sending long Text messages as concatenated segments
Sending Binary messages
Sending Flash SMS
Sending WAP Push
Receiving SMS messages from mobile phones
Intuitive SMS building with fluent interface
Connection recovery with SMPP server
Working with any language including Arabic, Chinese, Hebrew, Russian, Greek and Unicode messages support
Reliable bulk SMS-sending more than 1000 messages per second rate
SSL/TLS support
Diagnostic Metrics
SMPP Server
SmppServer class is a main building block for anyone willing to provide SMPP services for customers. It allows developers to start SMPP server and accept plenty of concurrent connections from clients. Processing concatenated messages, tracking delivery reports and interacting with third-party load-balancers is fairly straightforward with the library. SmppServer is based on async- multi-thread architecture (Task-based asynchronous pattern) and provides an extremely high stability level.
Features
Multiple concurrent client connections support
Receiving SMS messages from connected clients
Sending Concatenated Text messages
Sending Delivery receipts
Message status query support
Message rate limit and throttling
Ability to forward received messages to next SMPP server
SSL/TLS support
Diagnostic Metrics
Tests availability of client with enquiry_link command
Proxy Protocol for load-balancing support
Message Types & Encodings
The Inetlab SMPP .NET component supports all necessary character sets including GSM 03.38 and Unicode. It operates in any language including Arabic, Chinese, Hebrew, Russian and Greek.
The following message types are supported:
Short text SMS
Long text messages as concatenated SMS parts
Binary messages
USSD operations
Flash SMS
WAP Push
PDU creation and manipulation
SMS messages are transferred over a network as Protocol Data Units (PDUs). The Inetlab SMPP library implements all types of SMPP commands and responses. It includes SMS builder fluent-interface for simple and intuitive PDU creation without worrying about message text length. There is also a special MessageComposer class helping to get full message text from a sequence of concatenated parts.
Logging
Logging features are very important for any developer to test and debug the software at development and production stage. The Inetlab SMPP component includes logging interfaces for peeking inside each operation performed. Well-formed log saves your time!
public static async Task SendHelloWorld()
{
using (SmppClient client = new SmppClient())
{
if (await client.Connect("localhost", 7777))
{
BindResp bindResp = await client.Bind("1", "2");
if (bindResp.Header.Status == CommandStatus.ESME_ROK)
{
var submitResp = await client.Submit(
SMS.ForSubmit()
.From("111")
.To("222")
.Coding(DataCodings.UCS2)
.Text("Hello World!"));
if (submitResp.All(x => x.Header.Status == CommandStatus.ESME_ROK))
{
client.Logger.Info("Message has been sent.");
}
}
await client.Disconnect();
}
}
}
Public Shared Async Function SendHelloWorld() As Task
Using client As New SmppClient()
If Await client.Connect("localhost", 7777) Then
Dim bindResp As BindResp = Await client.Bind("1", "2")
If bindResp.Header.Status = CommandStatus.ESME_ROK Then
Dim submitResp = Await client.Submit(SMS.ForSubmit().From("111").To("222").Coding(DataCodings.UCS2).Text("Hello World!"))
If submitResp.All(Function(x) x.Header.Status = CommandStatus.ESME_ROK) Then
client.Logger.Info("Message has been sent.")
End If
End If
Await client.Disconnect()
End If
End Using
End Function
private readonly MessageComposer _composer = new MessageComposer();
private void client_evDeliverSm(object sender, DeliverSm data)
{
try
{
//Check if we received Delivery Receipt
if (data.MessageType == MessageTypes.SMSCDeliveryReceipt)
{
//Get MessageId of delivered message
string messageId = data.Receipt.MessageId;
MessageState deliveryStatus = data.Receipt.State;
}
else
{
// Receive incoming message and try to concatenate all parts
if (data.Concatenation != null)
{
_composer.AddMessage(data);
_log.Info(
"DeliverSm part received : Sequence: {0} SourceAddr: {1} Concatenation ( {2} ) Coding: {3} Text: {4}",
data.Header.Sequence, data.SourceAddress, data.Concatenation, data.DataCoding,
_client.EncodingMapper.GetMessageText(data));
if (_composer.IsLastSegment(data))
{
string fullMessage = _composer.GetFullMessage(data);
_log.Info("Full message: " + fullMessage);
}
}
else
{
_log.Info("DeliverSm received : Sequence: {0} SourceAddr : {1} Coding : {2} MessageText : {3}",
data.Header.Sequence, data.SourceAddress, data.DataCoding,
_client.EncodingMapper.GetMessageText(data));
}
}
}
catch (Exception ex)
{
data.Response.Header.Status = CommandStatus.ESME_RX_T_APPN;
_log.Error("Failed to process DeliverSm", ex);
}
}
Private ReadOnly _composer As New MessageComposer()
Private Sub client_evDeliverSm(ByVal sender As Object, ByVal data As DeliverSm)
Try
'Check if we received Delivery Receipt
If data.MessageType = MessageTypes.SMSCDeliveryReceipt Then
'Get MessageId of delivered message
Dim messageId As String = data.Receipt.MessageId
Dim deliveryStatus As MessageState = data.Receipt.State
Else
' Receive incoming message and try to concatenate all parts
If data.Concatenation IsNot Nothing Then
_composer.AddMessage(data)
_log.Info("DeliverSm part received : Sequence: {0} SourceAddr: {1} Concatenation ( {2} ) Coding: {3} Text: {4}", data.Header.Sequence, data.SourceAddress, data.Concatenation, data.DataCoding, _client.EncodingMapper.GetMessageText(data))
If _composer.IsLastSegment(data) Then
Dim fullMessage As String = _composer.GetFullMessage(data)
_log.Info("Full message: " & fullMessage)
End If
Else
_log.Info("DeliverSm received : Sequence: {0} SourceAddr : {1} Coding : {2} MessageText : {3}", data.Header.Sequence, data.SourceAddress, data.DataCoding, _client.EncodingMapper.GetMessageText(data))
End If
End If
Catch ex As Exception
data.Response.Header.Status = CommandStatus.ESME_RX_T_APPN
_log.Error("Failed to process DeliverSm", ex)
End Try
End Sub
public class SmppServerSample
{
private readonly SmppServer _server = new SmppServer(new IPEndPoint(IPAddress.Any, 7777));
private readonly IServerMessageStore _messageStore;
public SmppServerSample()
{
_messageStore = new DummyMessageStore();
_server.evClientBind += ServerOnClientBind;
_server.evClientSubmitSm += ServerOnClientSubmitSm;
_server.Start();
}
private void ServerOnClientBind(object sender, SmppServerClient client, Bind pdu)
{
//Set server name.
pdu.Response.SystemId = "MySMPPServer";
//check if client is allowed to create SMPP session.
if (IsClientAllowed(pdu))
{
Console.WriteLine($"Client {client.SystemID} has bound");
//Check if bound client can receive messages
if (client.BindingMode == ConnectionMode.Transceiver || client.BindingMode == ConnectionMode.Receiver)
{
//Start messages delivery
Task messagesTask = DeliverMessagesAsync(client, pdu);
}
}
else
{
Console.WriteLine($"New session from client {client.SystemID} is denied.");
pdu.Response.Header.Status = CommandStatus.ESME_RBINDFAIL;
}
}
private bool IsClientAllowed(Bind pdu)
{
//allow only one connection for one SMPP account.
int activeSessions = _server.ConnectedClients.Count(c => c.SystemID == pdu.SystemId);
bool alreadyConnected = activeSessions > 0;
if (alreadyConnected) return false;
return pdu.SystemId == pdu.Password;
}
private async Task DeliverMessagesAsync(SmppServerClient client, Bind pdu)
{
var messages = _messageStore.GetMessagesForClient(pdu.SystemId, pdu.SystemType);
foreach (TextMessage message in messages)
{
var pduBuilder = SMS.ForDeliver()
.From(message.PhoneNumber)
.To(message.ServiceAddress)
.Text(message.Text);
var responses = await client.Deliver(pduBuilder);
if (responses.All(x => x.Header.Status == CommandStatus.ESME_ROK))
{
_messageStore.MessageWasDelivered(message.Id);
}
}
Console.WriteLine($"Dummy messages have been sent to the client with systemId {pdu.SystemId}.");
}
private void ServerOnClientSubmitSm(object sender, SmppServerClient client, SubmitSm pdu)
{
Console.WriteLine($"Inbound message from {client.SystemID}");
//Set server message id for the pdu;
pdu.Response.MessageId = Guid.NewGuid().ToString().Substring(0, 8);
}
//Represents message storage. You need to implement this interface with desired database.
public interface IServerMessageStore
{
IEnumerable<TextMessage> GetMessagesForClient(string systemId, string systemType);
void MessageWasDelivered(string messageId);
}
public class TextMessage
{
public string Id { get; set; }
public string PhoneNumber { get; set; }
public string Text { get; set; }
public string ServiceAddress { get; set; }
}
public class DummyMessageStore : IServerMessageStore
{
readonly List<TextMessage> _messages = new List<TextMessage>();
public DummyMessageStore()
{
_messages.Add(new TextMessage
{
Id = "1",
ServiceAddress = "5555",
PhoneNumber = "7917123456",
Text = "test 1"
});
}
public IEnumerable<TextMessage> GetMessagesForClient(string systemId, string systemType)
{
return _messages;
}
public void MessageWasDelivered(string messageId)
{
_messages.RemoveAll(m => m.Id == messageId);
}
}
}
Public Class SmppServerSample
Private ReadOnly _server As New SmppServer(New IPEndPoint(IPAddress.Any, 7777))
Private ReadOnly _messageStore As IServerMessageStore
Public Sub New()
_messageStore = New DummyMessageStore()
AddHandler _server.evClientBind, AddressOf ServerOnClientBind
AddHandler _server.evClientSubmitSm, AddressOf ServerOnClientSubmitSm
_server.Start()
End Sub
Private Sub ServerOnClientBind(ByVal sender As Object, ByVal client As SmppServerClient, ByVal pdu As Bind)
'Set server name.
pdu.Response.SystemId = "MySMPPServer"
'check if client is allowed to create SMPP session.
If IsClientAllowed(pdu) Then
Console.WriteLine($"Client {client.SystemID} has bound")
'Check if bound client can receive messages
If client.BindingMode = ConnectionMode.Transceiver OrElse client.BindingMode = ConnectionMode.Receiver Then
'Start messages delivery
Dim messagesTask As Task = DeliverMessagesAsync(client, pdu)
End If
Else
Console.WriteLine($"New session from client {client.SystemID} is denied.")
pdu.Response.Header.Status = CommandStatus.ESME_RBINDFAIL
End If
End Sub
Private Function IsClientAllowed(ByVal pdu As Bind) As Boolean
'allow only one connection for one SMPP account.
Dim activeSessions As Integer = _server.ConnectedClients.Count(Function(c) c.SystemID = pdu.SystemId)
Dim alreadyConnected As Boolean = activeSessions > 0
If alreadyConnected Then
Return False
End If
Return pdu.SystemId = pdu.Password
End Function
Private Async Function DeliverMessagesAsync(ByVal client As SmppServerClient, ByVal pdu As Bind) As Task
Dim messages = _messageStore.GetMessagesForClient(pdu.SystemId, pdu.SystemType)
For Each message As TextMessage In messages
Dim pduBuilder = SMS.ForDeliver().From(message.PhoneNumber).To(message.ServiceAddress).Text(message.Text)
Dim responses = Await client.Deliver(pduBuilder)
If responses.All(Function(x) x.Header.Status = CommandStatus.ESME_ROK) Then
_messageStore.MessageWasDelivered(message.Id)
End If
Next message
Console.WriteLine($"Dummy messages have been sent to the client with systemId {pdu.SystemId}.")
End Function
Private Sub ServerOnClientSubmitSm(ByVal sender As Object, ByVal client As SmppServerClient, ByVal pdu As SubmitSm)
Console.WriteLine($"Inbound message from {client.SystemID}")
'Set server message id for the pdu;
pdu.Response.MessageId = Guid.NewGuid().ToString().Substring(0, 8)
End Sub
'Represents message storage. You need to implement this interface with desired database.
Public Interface IServerMessageStore
Function GetMessagesForClient(ByVal systemId As String, ByVal systemType As String) As IEnumerable(Of TextMessage)
Sub MessageWasDelivered(ByVal messageId As String)
End Interface
Public Class TextMessage
Public Property Id() As String
Public Property PhoneNumber() As String
Public Property Text() As String
Public Property ServiceAddress() As String
End Class
Public Class DummyMessageStore
Implements IServerMessageStore
Private ReadOnly _messages As New List(Of TextMessage)()
Public Sub New()
_messages.Add(New TextMessage With {
.Id = "1",
.ServiceAddress = "5555",
.PhoneNumber = "7917123456",
.Text = "test 1"
})
End Sub
Public Function GetMessagesForClient(ByVal systemId As String, ByVal systemType As String) As IEnumerable(Of TextMessage) Implements IServerMessageStore.GetMessagesForClient
Return _messages
End Function
Public Sub MessageWasDelivered(ByVal messageId As String) Implements IServerMessageStore.MessageWasDelivered
_messages.RemoveAll(Function(m) m.Id = messageId)
End Sub
End Class
End Class
Prices and License Options
TRIAL VERSION
Inetlab.SMPP is free for development and testing usage.
The Trial version may be used for private evaluation purposes only.
Developers can test the functionality of Inetlab.SMPP without paying for a license.
The library should not be published in any internet nor intranet project until an appropriate license is purchased.