|
@@ -0,0 +1,168 @@
|
|
|
+/**
|
|
|
+ *支持自动连接的socket客户端,
|
|
|
+ *支持tls
|
|
|
+ */
|
|
|
+package util
|
|
|
+
|
|
|
+import (
|
|
|
+ "crypto/tls"
|
|
|
+ "errors"
|
|
|
+ "io"
|
|
|
+ "net"
|
|
|
+ "sync"
|
|
|
+ "sync/atomic"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+//
|
|
|
+const (
|
|
|
+ statusOnline = iota
|
|
|
+ statusOffline
|
|
|
+ statusReconnecting
|
|
|
+)
|
|
|
+
|
|
|
+//
|
|
|
+type AutoRCClient struct {
|
|
|
+ io.ReadWriteCloser
|
|
|
+ lock sync.RWMutex
|
|
|
+ status int32
|
|
|
+ maxRetries int //重连允许次数
|
|
|
+ retryInterval time.Duration //重连间隔时间
|
|
|
+ network string //网络类型
|
|
|
+ serveraddr string //地址
|
|
|
+ onconnectedfn func() //连接成功后的通知函数
|
|
|
+ tlsconfig *tls.Config
|
|
|
+ dialtimeout time.Duration //
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+func NewAutoRCClient(network, serveraddr string,
|
|
|
+ config *tls.Config, maxretries int,
|
|
|
+ retryinterval time.Duration,
|
|
|
+ dialtimeout time.Duration,
|
|
|
+ connectedcallback func()) *AutoRCClient {
|
|
|
+ //
|
|
|
+ return &AutoRCClient{
|
|
|
+ network: network,
|
|
|
+ serveraddr: serveraddr,
|
|
|
+ lock: sync.RWMutex{},
|
|
|
+ maxRetries: maxretries,
|
|
|
+ status: statusOffline,
|
|
|
+ dialtimeout: dialtimeout,
|
|
|
+ retryInterval: retryinterval,
|
|
|
+ onconnectedfn: connectedcallback,
|
|
|
+ tlsconfig: config,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//连接
|
|
|
+func (t *AutoRCClient) Dial() error {
|
|
|
+ return t.DialWithTimeout(t.dialtimeout)
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+func (t *AutoRCClient) DialWithTimeout(d time.Duration) error {
|
|
|
+ if conn, err := t.dial4RCClient(d); err == nil {
|
|
|
+ t.ReadWriteCloser = conn
|
|
|
+ t.status = statusOnline
|
|
|
+ return nil
|
|
|
+ } else {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+func (t *AutoRCClient) dial4RCClient(d time.Duration) (io.ReadWriteCloser, error) {
|
|
|
+ var err error
|
|
|
+ var conn io.ReadWriteCloser
|
|
|
+ if t.tlsconfig == nil {
|
|
|
+ conn, err = net.DialTimeout(t.network, t.serveraddr, d)
|
|
|
+ } else {
|
|
|
+ conn, err = tls.DialWithDialer(&net.Dialer{
|
|
|
+ Timeout: d,
|
|
|
+ }, t.network, t.serveraddr, t.tlsconfig)
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return conn, nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+func (c *AutoRCClient) reConnect() error {
|
|
|
+ if !atomic.CompareAndSwapInt32(&c.status, statusOffline, statusReconnecting) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ conn, err := c.dial4RCClient(c.dialtimeout)
|
|
|
+ if err != nil {
|
|
|
+ atomic.StoreInt32(&c.status, statusOffline)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ c.ReadWriteCloser.Close() //关闭以前的连接
|
|
|
+ c.ReadWriteCloser = conn
|
|
|
+ atomic.StoreInt32(&c.status, statusOnline)
|
|
|
+ if c.onconnectedfn != nil {
|
|
|
+ c.onconnectedfn() //执行后续操作
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+//读操作
|
|
|
+func (c *AutoRCClient) Read(b []byte) (int, error) {
|
|
|
+ c.lock.RLock()
|
|
|
+ defer c.lock.RUnlock()
|
|
|
+ for i := 0; i < c.maxRetries; i++ {
|
|
|
+ if atomic.LoadInt32(&c.status) == statusOnline {
|
|
|
+ n, err := c.ReadWriteCloser.Read(b)
|
|
|
+ if err == nil {
|
|
|
+ return n, err
|
|
|
+ }
|
|
|
+ switch err.(type) {
|
|
|
+ case *net.OpError:
|
|
|
+ atomic.StoreInt32(&c.status, statusOffline)
|
|
|
+ default:
|
|
|
+ if err.Error() == "EOF" {
|
|
|
+ atomic.StoreInt32(&c.status, statusOffline)
|
|
|
+ } else {
|
|
|
+ return n, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if atomic.LoadInt32(&c.status) == statusOffline {
|
|
|
+ if err := c.reConnect(); err != nil {
|
|
|
+ return -1, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if i < (c.maxRetries - 1) {
|
|
|
+ time.Sleep(c.retryInterval)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1, errors.New("超过了最大尝试连接次数")
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+func (c *AutoRCClient) Write(b []byte) (int, error) {
|
|
|
+ c.lock.RLock()
|
|
|
+ defer c.lock.RUnlock()
|
|
|
+ for i := 0; i < c.maxRetries; i++ {
|
|
|
+ if atomic.LoadInt32(&c.status) == statusOnline {
|
|
|
+ n, err := c.ReadWriteCloser.Write(b)
|
|
|
+ if err == nil {
|
|
|
+ return n, err
|
|
|
+ }
|
|
|
+ switch err.(type) {
|
|
|
+ case *net.OpError:
|
|
|
+ atomic.StoreInt32(&c.status, statusOffline)
|
|
|
+ default:
|
|
|
+ return n, err
|
|
|
+ }
|
|
|
+ } else if atomic.LoadInt32(&c.status) == statusOffline {
|
|
|
+ if err := c.reConnect(); err != nil {
|
|
|
+ return -1, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if i < (c.maxRetries - 1) {
|
|
|
+ time.Sleep(c.retryInterval)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1, errors.New("超过了最大尝试连接次数")
|
|
|
+}
|