linxgnu / gosmpp Goto Github PK
View Code? Open in Web Editor NEWSmpp (3.4) Client Library for Go
License: Apache License 2.0
Smpp (3.4) Client Library for Go
License: Apache License 2.0
I was trying to run SMSC by ./run.sh
inside example folder but I'm getting following error
➜ smsc git:(master) ✗ ./run.sh
smsc.cpp: In destructor ‘virtual SMPPSession::~SMPPSession()’:
smsc.cpp:646:41: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
logCommand("session aborted", "--");
^
smsc.cpp: In member function ‘void SMPPSession::logCommand(uint64_t, const char*)’:
smsc.cpp:1186:58: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 3 has type ‘uint64_t {aka long unsigned int}’ [-Wformat=]
sprintf(buf, "0x%08llx %s", cmdID, b.cmdString(cmdID));
^
smsc.cpp: In member function ‘void SMPPSession::logCommand(char*, const char*)’:
smsc.cpp:1201:33: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘uint64_t {aka long unsigned int}’ [-Wformat=]
printf("%s %s [s%06llx:%-15s] %s\n", buffer, direction, session_id,
~~~~~~~~~~
conn.getIP(), logline);
There is sometimes a deadlock when the connection is closed by server :
func (t *transmittable) close(state State) (err error) {
t.lock.Lock()
This function will call t.Cancel() (transmittable.go:50)
And so, the session is never released.
go run main.go
Getting Error.
2022/10/27 02:47:41 EOF
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x78 pc=0x3b5460]
goroutine 1 [running]:
github.com/linxGnu/gosmpp.(*Session).Close(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:92
main.sendingAndReceiveSMS.func5()
C:/Users/agan/Desktop/ezsmpp/main.go:63 +0x20
panic({0x3c9aa0, 0x4bf9b0})
C:/Program Files/Go/src/runtime/panic.go:1038 +0x215
github.com/linxGnu/gosmpp.(*Session).bound(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:71
github.com/linxGnu/gosmpp.(*Session).Transceiver(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:87
main.sendingAndReceiveSMS(0x2f8005)
C:/Users/agan/Desktop/ezsmpp/main.go:68 +0x372
main.main()
C:/Users/agan/Desktop/ezsmpp/main.go:19 +0x3e
exit status 2
module ezsmpp
go 1.17
require github.com/linxGnu/gosmpp v0.1.4-rc24
require golang.org/x/text v0.4.0 // indirect
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/linxGnu/gosmpp v0.1.4-rc24 h1:cAZXUHNGphurSPWRl1RwKDJbaTwxGEEw2jU/QveAfAM=
github.com/linxGnu/gosmpp v0.1.4-rc24/go.mod h1:GDPzhhhZOM/hDQWIEp11VnK1KFcgDxaJ/eRen/YF+9Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/linxGnu/gosmpp"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"
)
func main() {
var wg sync.WaitGroup
wg.Add(6)
sendingAndReceiveSMS(&wg)
wg.Wait()
}
func sendingAndReceiveSMS(wg *sync.WaitGroup) {
defer wg.Done()
auth := gosmpp.Auth{
SMSC: "smscsim.melroselabs.com:2775",
SystemID: "169994",
Password: "EDXPJU",
SystemType: "",
}
trans, err := gosmpp.NewSession(
gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
gosmpp.Settings{
EnquireLink: 5 * time.Second,
ReadTimeout: 10 * time.Second,
OnSubmitError: func(_ pdu.PDU, err error) {
log.Fatal("SubmitPDU error:", err)
},
OnReceivingError: func(err error) {
fmt.Println("Receiving PDU/Network error:", err)
},
OnRebindingError: func(err error) {
fmt.Println("Rebinding but error:", err)
},
OnPDU: handlePDU(),
OnClosed: func(state gosmpp.State) {
fmt.Println(state)
},
}, 5*time.Second)
if err != nil {
log.Println(err)
}
defer func() {
_ = trans.Close()
}()
// sending SMS(s)
for i := 0; i < 1800; i++ {
if err = trans.Transceiver().Submit(newSubmitSM()); err != nil {
fmt.Println(err)
}
time.Sleep(time.Second)
}
}
func handlePDU() func(pdu.PDU, bool) {
concatenated := map[uint8][]string{}
return func(p pdu.PDU, _ bool) {
switch pd := p.(type) {
case *pdu.SubmitSMResp:
fmt.Printf("SubmitSMResp:%+v\n", pd)
case *pdu.GenericNack:
fmt.Println("GenericNack Received")
case *pdu.EnquireLinkResp:
fmt.Println("EnquireLinkResp Received")
case *pdu.DataSM:
fmt.Printf("DataSM:%+v\n", pd)
case *pdu.DeliverSM:
fmt.Printf("DeliverSM:%+v\n", pd)
log.Println(pd.Message.GetMessage())
// region concatenated sms (sample code)
message, err := pd.Message.GetMessage()
if err != nil {
log.Fatal(err)
}
totalParts, sequence, reference, found := pd.Message.UDH().GetConcatInfo()
if found {
if _, ok := concatenated[reference]; !ok {
concatenated[reference] = make([]string, totalParts)
}
concatenated[reference][sequence-1] = message
}
if !found {
log.Println(message)
} else if parts, ok := concatenated[reference]; ok && isConcatenatedDone(parts, totalParts) {
log.Println(strings.Join(parts, ""))
delete(concatenated, reference)
}
// endregion
}
}
}
func newSubmitSM() *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress("00" + "522241")
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress("99" + "522241")
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetMessageWithEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ", data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM
}
func isConcatenatedDone(parts []string, total byte) bool {
for _, part := range parts {
if part != "" {
total--
}
}
return total == 0
}
Receiver
should have the possibility to know if a PDU is handled by ESME before sending auto response, because if the ESME does not handle DataSM
(for example), the response should be a GenericNack
, not a DataSM_Resp
.
I see by default we use a non-TLS dialer, which in turn relies on net.Conn
for instantiating and subsequently defining a significant portion of the downstream buffer reads/etc.
Is there a working example or any plans to support a crypto/tls Dialer?
I've attempted to do this myself, but I'm getting some odd PDU related errors which I'm in the process of trying to debug.
It seems a significant portion of the libraries depend on the root connection working from the net.Conn
return type. Has anyone had successful forks of implementing the above?
Can u please provide example for smpp client connection handling all pdu...Earlier it was there in test folder now it has been removed..
Thanks
Lines 150 to 152 in cc603b5
This a simple algorithm not correct.
e.q: Wrong for Emoji
>>> '😈'.encode('utf-16-be')
b'\xd8=\xde\x08'
>>> len(_)
4
I suspect this is because the example was built for 0.1.3 in mind.
0.1.4-rc29 gives an immediate segfault on session closure (possibly an uncaptured error):
2022/06/23 10:44:16 EOF
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x78 pc=0x4df8a0]
goroutine 19 [running]:
github.com/linxGnu/gosmpp.(*Session).Close(...)
/home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:93
main.sendingAndReceiveSMS.func5()
tools/smpp-client/main.go:65 +0x20
panic({0x4f4460, 0x5e5e40})
/usr/local/go/src/runtime/panic.go:838 +0x207
github.com/linxGnu/gosmpp.(*Session).bound(...)
/home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:72
github.com/linxGnu/gosmpp.(*Session).Transceiver(...)
/home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:88
main.sendingAndReceiveSMS(0x0?)
tools/smpp-client/main.go:71 +0x3da
created by main.main
tools/smpp-client/main.go:20 +0x76
Looks as though the session state isn't instantiated as the code is breaking here: https://github.com/linxGnu/gosmpp/blob/master/session.go#L90-L96
Hi, @linxGnu
When the TCPIPConnection
got Exception.ConnectionClosingDueToError
, how can I get it in my listener? I want to reconnect if Exception.ConnectionClosingDueToError
happend.
Thanks.
I need to handle binary messages.
It appears that the data coding of DeliverSM PDU is not exposed, and in codingMap
(codings.go:209)
, data coding 0x02 and 0x04 are not supported (datacoding codes for 8-bit binary).
It's the same problem for SubmitSM.
Have you ever met this issue ?
Do you intend to support binary messages in the future ?
I can make a pull request if you wish, any suggestions of implementation are welcome ;).
I tried to create a session manager.
package smpp
import (
"errors"
"github.com/sujit-baniya/smpp/pdu"
"github.com/sujit-baniya/xid"
"time"
)
type ManagerConfig struct {
Name string
Auth Auth
Settings Settings
RebindDuration time.Duration
Mode string
TPS int
MaxSession int
}
type Manager struct {
Name string
ID string
Session map[string]*Session
Config ManagerConfig
MaxSession int
}
func NewManager(cfg ManagerConfig) *Manager {
if cfg.TPS == 0 {
cfg.TPS = 10
}
if cfg.MaxSession == 0 {
cfg.MaxSession = 4
}
return &Manager{
Name: cfg.Name,
ID: xid.New().String(),
Config: cfg,
}
}
func (m *Manager) Start() {
if m.Session == nil {
m.AddSession()
}
// m.balancer.balance(m.pdu)
}
func (m *Manager) AddSession(noOfSession ...int) string {
if len(noOfSession) == 0 {
return m.addSession()
}
if noOfSession[0] > m.MaxSession {
panic(errors.New("Can't create more than allowed no of sessions."))
}
if (len(m.Session) + noOfSession[0]) > m.MaxSession {
panic(errors.New("There are active sessions. Can't create more than allowed no of sessions."))
}
for i := 0; i < noOfSession[0]; i++ {
m.addSession()
}
return ""
}
func (m *Manager) addSession() string {
if m.Session == nil {
m.Session = make(map[string]*Session)
}
session, err := NewSession(
TRXConnector(NonTLSDialer, m.Config.Auth),
m.Config.Settings,
m.Config.RebindDuration,
)
if err != nil {
panic(err)
}
m.Session[session.ID] = session
return session.ID
}
func (m *Manager) RemoveSession(sessionId ...string) error {
if len(sessionId) > 0 {
return m.close(sessionId[0])
} else {
return m.Close()
}
}
func (m *Manager) close(sessionId string) error {
if !m.Session[sessionId].IsClosed() {
err := m.Session[sessionId].close()
if err != nil {
return err
}
}
// delete(m.Session, sessionId)
return nil
}
func (m *Manager) Close(noOfSession ...int) error {
if len(noOfSession) == 0 {
for id := range m.Session {
err := m.close(id)
if err != nil {
return err
}
}
}
return nil
}
func (m *Manager) Rebind(sessionId ...string) {
if len(sessionId) == 0 {
if session, ok := m.Session[sessionId[0]]; ok {
session.rebind()
}
}
}
func (m *Manager) Submit(pd pdu.PDU, sessionId ...string) error {
session := m.getSession(sessionId...)
if session == nil {
return errors.New("Session not found")
}
return m.getSession(sessionId...).Transceiver().Submit(pd)
}
func (m *Manager) getSession(sessionId ...string) *Session {
if len(sessionId) > 0 {
if session, ok := m.Session[sessionId[0]]; ok {
return session
}
}
for _, session := range m.Session {
return session
}
return nil
}
Can anyone suggest me how can I implement load balancing between the session?
All real SMSC that I've worked with implement a mechanism for receiving a certain number of pdus without blocking the ESME until the replies are sent. The outgoing send window is a parameter when defining SMSC large account and by default is usually 1. Setting send window to a number higher than 1 help ESMEs to improve the throughput by allowing them to send a specific number of submit_sm pdus without blocking waiting for reply. If the send window gets full (meaning that ESME sent X number of submit_sms for which SMSC has not sent yet a reply), ESME will need to wait for submit_sm_resps to arrive before sending another submit_sm. In the ESME exceeds the limit set on SMSC settings, the SMSC will start replying with ESME_RSUBMITFAILED or ESME_RMSGQFUL. This mechanism acts like a sliding send window.
It would be great if the library would implement this feature to limit (by block when reached) the number of submit_sms sent without receiving a reply.
I notice that UDH was not always detected correctly when deliver_sm is used. I believe the issue is when the ESM Class byte doesn't equal 0x40.
DeliverSM.go line 87
err = c.Message.Unmarshal(b, c.EsmClass == data.SM_UDH_GSM)
Can you modify DeliverSM.go to use bitwise to detect if the UDH Indicator bit is set ? Like it does for submit_sm.
SubmitSM.go line 125
err = c.Message.Unmarshal(b, (c.EsmClass&data.SM_UDH_GSM) > 0)
A quick search show that SubmitMulti.go also uses the same comparison as DeliverSM
Hi All.
In version 1.4.0+ cant send a long messages.
func newSubmitSM() *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress("xxx-xxx-xx")
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress("xxx-xxx-xx")
//submitSM := pdu.NewLongMessage().(*pdu.SubmitSM)
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetLongMessageWithEnc("Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text eve the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not on e centuries, but also the leap into electronic typesetting, remaining essentially unchanged.", data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM
}
sends an empty message. Cant Send a sms messages with more than 160 symbols.
I'm wondering when I call GetSequenceNumber, and Transceiver().Submit, are they thread safe or I should use Mutext to prevent multi access to these functions?
I used following piece of code:
enc := data.FindEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ")
fmt.Println(enc)
Above code results: &{}
instead of actual encoding interface among:
var (
// GSM7BIT is gsm-7bit encoding.
GSM7BIT Encoding = &gsm7bit{}
// ASCII is ascii encoding.
ASCII Encoding = &ascii{}
// LATIN1 encoding.
LATIN1 Encoding = &iso88591{}
// CYRILLIC encoding.
CYRILLIC Encoding = &iso88595{}
// HEBREW encoding.
HEBREW Encoding = &iso88598{}
// UCS2 encoding.
UCS2 Encoding = &ucs2{}
)
Am I missing something?
transmitter, err := gosmpp.NewTransmitterSession()
transmitter.Transmitter().Submit()
type TransmitSettings struct {
// WriteTimeout is timeout/deadline for submitting PDU.
WriteTimeout time.Duration
// EnquireLink periodically sends EnquireLink to SMSC.
// The duration must not be smaller than 1 minute.
//
// Zero duration disables auto enquire link.
EnquireLink time.Duration
// OnSubmitError notifies fail-to-submit PDU with along error.
OnSubmitError func(pdu.PDU, error)
// OnRebindingError notifies error while rebinding.
OnRebindingError func(error)
// OnClosed notifies `closed` event due to State.
OnClosed func(State)
}
I did n’t find any way to get Message. I need to get MessageID after submission and use it as a tag. I look forward to your answer. Thanks!
I am trying to set the data coding scheme to 0xf6(246) to test SAT push messaging in a production environment. I couldn't find a way to provision the DCS explicitly for this. Could anyone please help here.. Thank you very much.
There's an issue in ShortMessage.Unmarshal()
.
Sometimes SMS-C sends the message in TLV field MessagePayload. In these cases the ShortMessage length is 0.
So in the function, if the udhi
is true
, udh.UnmarshalBinary()
returns an error.
A PR is coming (like winter ;)).
I can't figure out how to handle the Submit_SM_Resp in a Transmitter connection.
The SMPP specification (page 17) says that the Submit_SM_Resp is sent in the same connection of the transmitter, but there's no way to listen for the PDU responses in the TransmitSettings
.
Did I miss something in the library ?
hi @linxGnu,
can you provide an example for a long-lasting connection SMPP Receiver client?
I tried to check the Telco example folder, but is not in the repository anymore.
Thank you
in python3
>>> bytes.fromhex('60a876848d2662375f53524d53ef75284f59989d003100330039002e0032003051433002007f007f002067e58be28d2662374f59989d8be660c58bf756de590d00590045004d00583002007f007f00203010629751fb75ab60c5ff0c670d52a14e0d505cff014f7f7528624b673a84254e1a5385ff0c8db34e0d51fa62374ea48bdd8d39300167e54f59989d3001529e4e1a52a1ff0c514d6d4191cf770b75355f71300173a96e38620fff0c70b951fb00200068007400740070003a002f002f0075002e00310030003000310030002e0063006e002f006b00680064006400780020ff0c9a6c4e0a62e567093011').decode('utf-16-be')
'您的账户当前可用余额139.20元。\x7f\x7f 查询账户余额详情请回复YEMX。\x7f\x7f 【抗击疫情,服务不停!使用手机营业厅,足不出户交话费、查余额、办业务,免流量看电影、玩游戏,点击 http://u.10010.cn/khddx ,马上拥有】'
new line is an unexpected result
do you have example about smpp server ?
Hi @linxGnu ,
can you please provide sample for SMPP Receiver/Transmitter client connection?. i think previously u had given in examples folder but i can't find it now.
Thanks in advance.
I installed go package go get -u github.com/linxGnu/gosmpp
And used file https://github.com/linxGnu/gosmpp/blob/master/example/main.go
But running go build
I'm getting following error:
main.go:10:2: case-insensitive import collision: "github.com/linxGnu/gosmpp/data" and "github.com/linxGnu/gosmpp/Data"
main.go:11:2: case-insensitive import collision: "github.com/linxGnu/gosmpp/pdu" and "github.com/linxGnu/gosmpp/PDU"
How would I fix it?
When sending multiple messages, how can I identify parent message of concatenated SMS so that I could do further processing of parent message
Some updates to run project
1. In the run.sh file
g++ smsc.cpp -o smsc && ./smsc
to
g++ -std=c++11 smsc.cpp -o smsc && ./smsc
in my case smsc.cpp does not compile, have 2 errors and around 20 warnings. (macos, default installed g++)
2. In file main.go change
auth := gosmpp.Auth{
SMSC: "smscsim.melroselabs.com:2775", -> "localhost:2775"
...
3. Is it possible to write whole server in go for testing?
Could you expose publicly gosmpp.data.codings? Data coding/decoding functions are needed for handling message_payload TLV. Some SMSCs accept or deliver payloads in this field instead of short_message. The coding/decoding of the payloads needs to be performed similar to short_message pdu field. It would be far more flexible to also have access to gosmpp.data.Codings.
Please check also SMPP spec: https://smpp.org/SMPP_v3_4_Issue1_2.pdf, section 5.3.2.32 for info about message_payload parameter.
On receiving response from SMSC, following response is received on https://github.com/linxGnu/gosmpp/blob/master/example/main.go#L109
id:8c849fc69a961d59f129aadefe130ff22051bd325f736bd85de93df2ed83c698 sub:000 dlvrd:001 submit date:2011071143 done date:2011071143 stat:DELIVRD err:000 text:@
How can I convert this to struct
Is there any example for sending multipart or concatenated SMS
Hi @linxGnu
When sending sms periodically (every 10 seconds) , then i turned off internet connection(wi-fi).
There is no error when send sms. How detect it?
I'm trying to modify example/main.go to send one message instead of loop.
package main
import (
"fmt"
"log"
"sync"
"time"
"github.com/linxGnu/gosmpp"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go sendingAndReceiveSMS(&wg)
wg.Wait()
}
func sendingAndReceiveSMS(wg *sync.WaitGroup) {
defer wg.Done()
auth := gosmpp.Auth{
SMSC: "192.168.88.250:2775",
SystemID: "522241",
Password: "UUDHWB",
SystemType: "",
}
trans, err := gosmpp.NewTransceiverSession(gosmpp.NonTLSDialer, auth, gosmpp.TransceiveSettings{
EnquireLink: 5 * time.Second,
OnSubmitError: func(p pdu.PDU, err error) {
log.Fatal(err)
},
OnReceivingError: func(err error) {
fmt.Println(err)
},
OnRebindingError: func(err error) {
fmt.Println(err)
},
OnPDU: handlePDU(),
OnClosed: func(state gosmpp.State) {
fmt.Println(state)
},
}, 5*time.Second)
if err != nil {
log.Fatal(err)
}
defer func() {
_ = trans.Close()
}()
// sending SMS(s)
if err = trans.Transceiver().Submit(newSubmitSM()); err != nil {
log.Fatal(err)
}
}
func handlePDU() func(pdu.PDU, bool) {
return func(p pdu.PDU, responded bool) {
switch pd := p.(type) {
case *pdu.SubmitSMResp:
fmt.Printf("SubmitSMResp:%+v\n", pd)
case *pdu.GenerickNack:
fmt.Println("GenericNack Received")
case *pdu.EnquireLinkResp:
fmt.Println("EnquireLinkResp Received")
case *pdu.DataSM:
fmt.Printf("DataSM:%+v\n", pd)
case *pdu.DeliverSM:
fmt.Printf("DeliverSM:%+v\n", pd)
fmt.Println(pd.Message.GetMessage())
}
}
}
func newSubmitSM() *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress("00" + "522241")
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress("99" + "522241")
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetMessageWithEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ", data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM
}
But I'm getting error
read tcp 192.168.88.248:52461->192.168.88.250:2775: use of closed network connection
How would I fix it?
Also is there any example where I can connect to SMSC once and send as many messages to different numbers?
Actually I'm new to go and little knowledge of smpp.
i cant use message_payload on gosmpp. when I check on tcpdump, I found different from openSMPP and goSMPP. in openSMPP have Optional parameter payload but in goSMPP is nothing
Thank you for this great library.
I am trying to implement this library along with REST.
User might send any text message with different message encoding.
Is it possible to detect the encoding?
By detecting the encoding, I wanted to clean the message content.
Some time the message content contains invisible empty characters which will change message encoding and thus message parts (length) might increase.
I'm testing gosmpp sample code against production SMSC and it returns EOF when calling gosmpp.NewSession using gosmpp v0.1.4-rc24
https://github.com/linxGnu/gosmpp/edit/master/example/main.go
Testing the same code with local simulator (Written in Java) works fine
When changing to TXConnector instead of TRXConnector in production returns below error:
Binding error. Command status: [13]. Please refer to: https://github.com/linxGnu/gosmpp/blob/master/data/pkg.go for more detail about this status code
package main
import (
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/linxGnu/gosmpp"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"
)
func sendingAndReceiveSMS(wg *sync.WaitGroup, auth gosmpp.Auth, source, dest, message string) {
defer wg.Done()
trans, err := gosmpp.NewSession(
gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
gosmpp.Settings{
EnquireLink: 5 * time.Second,
ReadTimeout: 10 * time.Second,
OnSubmitError: func(_ pdu.PDU, err error) {
fmt.Println("SubmitPDU error:", err.Error())
},
OnReceivingError: func(err error) {
fmt.Println("Receiving PDU/Network error:", err.Error())
},
OnRebindingError: func(err error) {
fmt.Println("Rebinding but error:", err.Error())
},
OnPDU: handlePDU(),
OnClosed: func(state gosmpp.State) {
fmt.Println(state)
},
}, 5*time.Second)
if err != nil {
fmt.Println("Error in sendingAndReceiveSMS: ", err.Error())
} else {
defer func() {
_ = trans.Close()
}()
}
// sending SMS(s)
if err == nil {
submitSMS := newSubmitSM(source, dest, message)
if err = trans.Transceiver().Submit(submitSMS); err != nil {
fmt.Println("Error in trans.Tranceiver(): ", err.Error())
}
time.Sleep(time.Second)
}
}
func handlePDU() func(pdu.PDU, bool) {
concatenated := map[uint8][]string{}
return func(p pdu.PDU, _ bool) {
switch pd := p.(type) {
case *pdu.SubmitSMResp:
fmt.Printf("SubmitSMResp:%+v\n", pd)
case *pdu.GenericNack:
fmt.Println("GenericNack Received")
case *pdu.EnquireLinkResp:
fmt.Println("EnquireLinkResp Received")
case *pdu.DataSM:
fmt.Printf("DataSM:%+v\n", pd)
case *pdu.DeliverSM:
fmt.Printf("DeliverSM:%+v\n", pd)
log.Println(pd.Message.GetMessage())
// region concatenated sms (sample code)
message, err := pd.Message.GetMessage()
if err != nil {
fmt.Println("Error in pd.Message.GetMessage(): ", err.Error())
}
totalParts, sequence, reference, found := pd.Message.UDH().GetConcatInfo()
if found {
if _, ok := concatenated[reference]; !ok {
concatenated[reference] = make([]string, totalParts)
}
concatenated[reference][sequence-1] = message
}
if !found {
log.Println(message)
} else if parts, ok := concatenated[reference]; ok && isConcatenatedDone(parts, totalParts) {
log.Println(strings.Join(parts, ""))
delete(concatenated, reference)
}
// endregion
}
}
}
func newSubmitSM(source, dest, message string) *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress(source)
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress(dest)
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetMessageWithEncoding(message, data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM
}
func isConcatenatedDone(parts []string, total byte) bool {
for _, part := range parts {
if part != "" {
total--
}
}
return total == 0
}
Tell me why there may be an error write: broken pipe?
A character was deleted by mistake for expected value in TestSubmitMulti, so the test fails with message encoding/hex: odd length hex string
All real SMSC that I've worked with have a rate limiting policy to prevent ESME to send submit_sm too fast. If the SMSC is detecting that ESME overpasses the allowed limiting (e.g. submits/sec), it will respond with ESME_RTHROTTLED and message will not be delivered. It would be useful if the library would support out-of-the box rate limiting the number of submits sent to SMSC.
Is there any Server example using this library?
go rum ./main.go
main.go:11:2: no required module provides package github.com/linxGnu/gosmpp/data;
In connect.go
line 89, the error returned on bind failure provides only int value of the command status, and the URL provided is not correct.
I suggest to provide a better error message, and ideally a specialized error that can provide the command status received.
It can be useful for an ESME to have a different behaviour on invalid password and on system error (for example).
Any example to send concatenated sms?
In GSM7 official table, the character ç
does not exist.
It would be great if it was possible to customize charset tables.
In my case, I could map ç
to 0x09
, to obtain something like this : hameÇon
, instead of getting an error.
What do you think ?
Of course I can make a PR if you like !
lookup reference document 3GPP 23.040
Hi @linxGnu
d1 := pdu.NewDestinationAddress()
there is no functions SetTon(), SetNpi()
how can i set this TON/NPI values?
When closing a transmitter or transceiver session, if a PDU has already been sent (EnquireLink for ex.), the session is not properly closed. In this case a i/o timeout
error is returned when t.conn.Write(marshal(pdu.NewUnbind()))
is called in transmitter.go
.
The reason is that the previous PDU write has set the WriteDeadline
through the call of t.conn.SetWriteTimeout()
in write()
function. So when the session is closed, the deadline is in the past when we try to write the UNBIND PDU.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.