func (c *client) Read() (pdu.Body, error) {
	select {
	case pdu := <-c.inbox:
		return pdu, nil
	case <-c.stop:
		return nil, io.EOF
	}
}
Read reads PDU binary data off the wire and returns it.
package smpp
import (
	"context"
	"crypto/tls"
	"io"
	"math"
	"sync"
	"time"
	"github.com/fiorix/go-smpp/smpp/pdu"
	"github.com/fiorix/go-smpp/smpp/pdu/pdufield"
)
type ConnStatus interface {
	Status() ConnStatusID
	Error() error
}
type connStatus struct {
	s   ConnStatusID
	err error
}
func (c *connStatus) Status() ConnStatusID { return c.s }
func (c *connStatus) Error() error         { return c.err }
type ConnStatusID uint8
const (
	Connected ConnStatusID = iota + 1
	Disconnected
	ConnectionFailed
	BindFailed
)
var connStatusText = map[ConnStatusID]string{
	Connected:        "Connected",
	Disconnected:     "Disconnected",
	ConnectionFailed: "Connection failed",
	BindFailed:       "Bind failed",
}
func (cs ConnStatusID) String() string {
	return connStatusText[cs]
}
type ClientConn interface {
	Bind() <-chan ConnStatus
	Closer
}
type RateLimiter interface {
	Wait(ctx context.Context) error
}
type client struct {
	Addr               string
	TLS                *tls.Config
	Status             chan ConnStatus
	BindFunc           func(c Conn) error
	EnquireLink        time.Duration
	EnquireLinkTimeout time.Duration
	RespTimeout        time.Duration
	BindInterval       time.Duration
	WindowSize         uint
	RateLimiter        RateLimiter
	inbox chan pdu.Body
	conn  *connSwitch
	stop  chan struct{}
	once  sync.Once
	lmctx context.Context
	eliTime time.Time
	eliMtx  sync.RWMutex
}
func (c *client) init() {
	c.conn = &connSwitch{}
	c.stop = make(chan struct{})
	if c.RateLimiter != nil {
		c.lmctx = context.Background()
	}
	if c.EnquireLink < 10*time.Second {
		c.EnquireLink = 10 * time.Second
	}
	if c.EnquireLinkTimeout == 0 {
		c.EnquireLinkTimeout = 3 * c.EnquireLink
	}
}
func (c *client) Bind() {
	delay := 1.0
	const maxdelay = 120.0
	for !c.closed() {
		eli := make(chan struct{})
		c.inbox = make(chan pdu.Body)
		conn, err := Dial(c.Addr, c.TLS)
		if err != nil {
			c.notify(&connStatus{
				s:   ConnectionFailed,
				err: err,
			})
			goto retry
		}
		c.conn.Set(conn)
		if err = c.BindFunc(c.conn); err != nil {
			c.notify(&connStatus{s: BindFailed, err: err})
			goto retry
		}
		go c.enquireLink(eli)
		c.notify(&connStatus{s: Connected})
		delay = 1
		for {
			p, err := c.conn.Read()
			if err != nil {
				c.notify(&connStatus{
					s:   Disconnected,
					err: err,
				})
				break
			}
			switch p.Header().ID {
			case pdu.EnquireLinkID:
				pResp := pdu.NewEnquireLinkRespSeq(p.Header().Seq)
				err := c.conn.Write(pResp)
				if err != nil {
					break
				}
			case pdu.EnquireLinkRespID:
				c.updateEliTime()
			default:
				c.inbox <- p
			}
		}
	retry:
		close(eli)
		c.conn.Close()
		close(c.inbox)
		delayDuration := c.BindInterval
		if delayDuration == 0 {
			delay = math.Min(delay*math.E, maxdelay)
			delayDuration = time.Duration(delay) * time.Second
		}
		c.trysleep(delayDuration)
	}
	close(c.Status)
}
func (c *client) enquireLink(stop chan struct{}) {
	c.updateEliTime()
	for {
		select {
		case <-time.After(c.EnquireLink):
			c.eliMtx.RLock()
			if time.Since(c.eliTime) >= c.EnquireLinkTimeout {
				c.conn.Write(pdu.NewUnbind())
				c.conn.Close()
				c.eliMtx.RUnlock()
				return
			}
			c.eliMtx.RUnlock()
			err := c.conn.Write(pdu.NewEnquireLink())
			if err != nil {
				return
			}
		case <-stop:
			return
		case <-c.stop:
			return
		}
	}
}
func (c *client) updateEliTime() {
	c.eliMtx.Lock()
	c.eliTime = time.Now()
	c.eliMtx.Unlock()
}
func (c *client) notify(ev ConnStatus) {
	select {
	case c.Status <- ev:
	default:
	}
}