linxGnu / gosmpp

Smpp (3.4) Client Library for Go
Apache License 2.0
154 stars 60 forks source link

Reconnecting to SMPP Server #154

Closed usmonzodasomon closed 1 month ago

usmonzodasomon commented 1 month ago

Hello, I am getting the error: SubmitPDU error: write tcp smsc_ip_and_port->server_ip_port: write: broken pipe. The value of the rebinding Interval = 5 seconds:

session, err := gosmpp.NewSession(
        gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
        getSettings(handler),
        5*time.Second)

Restarting the program solves the problem for a while. Please help me.

laduchesneau commented 1 month ago

Hi,

Broken pipe points to a network issue, the library should handle reconnect automatically if set correctly. Can you share your getSettings() function ?

Thanks

usmonzodasomon commented 1 month ago

Yes, sure:

func getSettings(handler handler.Handler) gosmpp.Settings { return gosmpp.Settings{ EnquireLink: 15 time.Second,

    ReadTimeout: 30 * time.Second,

    OnSubmitError: func(p pdu.PDU, err error) {
        logger.Logger.Error(fmt.Sprintf("SubmitPDU error: %s", err.Error()))
        logger.Logger.Debug(fmt.Sprintf("%+v\n", p))
    },

    OnReceivingError: func(err error) {
        logger.Logger.Error(fmt.Sprintf("Receiving error: %s", err.Error()))
    },

    OnRebindingError: func(err error) {
        logger.Logger.Error(fmt.Sprintf("Rebinding error: %s", err.Error()))
    },

    OnAllPDU: handler.HandlePDU(),

    OnClosed: func(state gosmpp.State) {
        logger.Logger.Info(fmt.Sprintf("Session closed: %s", state.String()))
    },
}

}

laduchesneau commented 1 month ago

Ok, OnSubmitError is being called because the network is down and you cant submit anymore.

OnSubmitError: func(p pdu.PDU, err error) {
    logger.Logger.Error(fmt.Sprintf("SubmitPDU error: %s", err.Error()))
    logger.Logger.Debug(fmt.Sprintf("%+v\n", p))
},

When using OnAllPDU, all PDUs need to be handled by the client. Are you responding to all PDUs, even enquire_links ?

func handlePDU() func(pdu.PDU) (pdu.PDU, bool) {
    return func(p pdu.PDU) (pdu.PDU, bool) {
        switch pd := p.(type) {
        case *pdu.Unbind:
        ...
        case *pdu.EnquireLink:
            fmt.Println("EnquireLink Received")
            return pd.GetResponse(), false

        ...
    }
}
usmonzodasomon commented 1 month ago

I only respond to these PDUs:

func HandlePDU() func(pdu.PDU) (pdu.PDU, bool) {
    return func(p pdu.PDU) (pdu.PDU, bool) {
        switch pd := p.(type) {
        case *pdu.Unbind:
            return h.Unbind(pd)
        case *pdu.DeliverSM:
            return h.DeliverSM(pd)
        case *pdu.QuerySMResp:
            return h.QuerySMResp(pd)
        case *pdu.DataSMResp:
            return h.DataSMResp(pd)
        }
        return nil, false
    }
}
laduchesneau commented 1 month ago

The enquire_link is a bind keep alive mechanism, your tcp stream would disconnect if no traffic after your timeout value of 30 seconds.

Here are my recommendation:

as to why it doesn't reconnect, that I do not know.

usmonzodasomon commented 1 month ago

Thank you for your reply. I'm thinking of adding EnquireLink processing, please help me with this: should I return an answer this way:

case *pdu.EnquireLink:
    return pd.GetResponse(), false

, or thus:

case *pdu.EnquireLink:
    return pd.GetResponse(), true

thanks!

usmonzodasomon commented 1 month ago

I added response to EnquireLink but it didn't solve my problem. At the moment, my OnAllPDU function looks like this:

func (h *Handler) Handle PDU() func(pdu.PDU) (pdu.PDU, bool) {
    return func(pdu.PDU) (pdu.PDU, bool) {
        switch pd := p.(type) {
        case *pdu.Unbind:
            return h.Unbind(pd)
        case *pdu.DeliverSM:
            return h.DeliverSM(pd)
        case *pdu.QuerySMResp:
            return h.QuerySMResp(pd)
        case *pdu.DataSMResp:
            return h.DataSMResp(pd)
        case *pdu.EnquireLink:
            return pd.GetResponse(), true
        }
        return nil, false
    }
}

I am also sending you my smpp settings:

func getSettings(handler *handler.Handler) gosmpp.Settings {
    return gosmpp.Settings{
        EnquireLink: 30 * time.Second,

        ReadTimeout: 60 * time.Second,

        OnSubmitError: func(p pdu.PDU, err error) {
            logger.Logger.Error(fmt.Sprintf("SubmitPDU error: %s", err.Error()))
            logger.Logger.Debug(fmt.Sprintf("%+v\n", p))
        },

        OnReceivingError: func(err error) {
            logger.Logger.Error(fmt.Sprintf("Receiving error: %s", err.Error()))
        },

        OnRebindingError: func(err error) {
            logger.Logger.Error(fmt.Sprintf("Rebinding error: %s", err.Error()))
        },

        OnAllPDU: handler.HandlePDU(),

        OnClosed: func(state gosmpp.State) {
            logger.Logger.Info(fmt.Sprintf("Session closed: %s", state.String()))
        },
    }
}

(I increased the ReadTimeout by 60 seconds)

laduchesneau commented 1 month ago

The return bool is if you want to close the bind, herefor by returning true, you are closing the bind.

You should only return true for Unbind request.

usmonzodasomon commented 1 month ago

Thanks, I changed it to false, I'll report the results later.

As far as I can reason, the EnquireLink request in my case is sent every 30 seconds. I wonder why the connection is not terminated immediately, but after a few hours.

usmonzodasomon commented 1 month ago

Hello, thank you for your help. I added the EnquireLink processing and I don't have the error anymore. I will send my handler below just in case:


func (h *Handler) Handle PDU() func(pdu.PDU) (pdu.PDU, bool) {
    return func(pdu.PDU) (pdu.PDU, bool) {
        switch pd := p.(type) {
        case *pdu.Unbind:
            return h.Unbind(pd)
        case *pdu.DeliverSM:
            return h.DeliverSM(pd)
        case *pdu.QuerySMResp:
            return h.QuerySMResp(pd)
        case *pdu.DataSMResp:
            return h.DataSMResp(pd)
        case *pdu.EnquireLink:
            return pd.GetResponse(), false
        }
        return nil, false
    }
}