github.com/binbinly/pkg@v0.0.11-0.20240321014439-f4fbf666eb0f/transport/ws/connect.go (about)

     1  package ws
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/http"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/binbinly/pkg/logger"
    11  	"github.com/gorilla/websocket"
    12  )
    13  
    14  // Connection 定义连接接口
    15  type Connection interface {
    16  	Start()                   //启动连接,让当前连接开始工作
    17  	Stop()                    //停止连接,结束当前连接状态
    18  	Context() context.Context //返回ctx,用于用户自定义的go程获取连接退出状态
    19  
    20  	GetID() uint64        //获取当前连接ID
    21  	GetUID() int          //获取当前连接鉴权ID
    22  	RemoteAddr() net.Addr //获取远程客户端地址信息
    23  
    24  	Send(ctx context.Context, mid int, data []byte) error      //发送消息
    25  	AsyncSend(ctx context.Context, mid int, data []byte) error //异步发送消息
    26  }
    27  
    28  const (
    29  	// Time allowed to read the next pong message from the peer.
    30  	pongWait = 60 * time.Second
    31  
    32  	// Send pings to peer with this period. Must be less than pongWait.
    33  	pingPeriod = (pongWait * 9) / 10
    34  )
    35  
    36  // wsConnection 连接
    37  type wsConnection struct {
    38  	mu sync.RWMutex
    39  	//当前连接的ID 也可以称作为SessionID,ID全局唯一
    40  	id  uint64
    41  	uid int
    42  	//告知该链接已经退出/停止的channel
    43  	ctx    context.Context
    44  	cancel context.CancelFunc
    45  	//消息管道,用于读、写两个goroutine之间的消息通信
    46  	msgChan chan []byte
    47  	//当前Conn属于哪个Server
    48  	server *wsServer
    49  	//websocket连接对象
    50  	conn *websocket.Conn
    51  	//websocket响应对象
    52  	writer http.ResponseWriter
    53  	//websocket请求对象
    54  	request *http.Request
    55  	//当前连接的关闭状态
    56  	isClosed bool
    57  }
    58  
    59  // NewConnect 创建连接的方法
    60  func NewConnect(s *wsServer, conn *websocket.Conn, id uint64, uid int) Connection {
    61  	return &wsConnection{
    62  		server:  s,
    63  		id:      id,
    64  		uid:     uid,
    65  		conn:    conn,
    66  		msgChan: make(chan []byte, s.Options().MaxMsgChanLen),
    67  	}
    68  }
    69  
    70  // startWriter 写消息Goroutine, 用户将数据发送给客户端
    71  func (c *wsConnection) startWriter() {
    72  	logger.Debug("[ws.write] Writer Goroutine is running")
    73  	// 心跳由客户端发送 ping 回复 pong
    74  	ticker := time.NewTicker(pingPeriod)
    75  	defer func() {
    76  		defer logger.Debugf("[ws.write] %v conn Writer exit!", c.RemoteAddr().String())
    77  		ticker.Stop()
    78  	}()
    79  
    80  	for {
    81  		select {
    82  		case data, ok := <-c.msgChan:
    83  			if !ok {
    84  				_ = c.conn.WriteMessage(websocket.CloseMessage, []byte("close"))
    85  				return
    86  			}
    87  			_ = c.conn.SetWriteDeadline(time.Now().Add(c.server.Options().WriteWait))
    88  			logger.Debugf("[ws.write] write msg:%v", string(data))
    89  			if err := c.conn.WriteMessage(websocket.TextMessage, data); err != nil {
    90  				logger.Warnf("[ws.write] conn.write err :%v", err)
    91  				break
    92  			}
    93  		case <-c.ctx.Done():
    94  			return
    95  		case <-ticker.C:
    96  			_ = c.conn.SetWriteDeadline(time.Now().Add(c.server.Options().WriteWait))
    97  			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
    98  				logger.Warnf("[ws.write] ticker ping wrr:%v", err)
    99  				return
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // startReader 读消息Goroutine,用于从客户端中读取数据
   106  func (c *wsConnection) startReader() {
   107  	logger.Debug("[ws.read] Reader Goroutine is running")
   108  	defer func() {
   109  		logger.Debugf("[ws.read] %v conn Reader exit!", c.RemoteAddr().String())
   110  		c.Stop()
   111  	}()
   112  
   113  	c.conn.SetReadLimit(int64(c.server.Options().MaxPacketSize))
   114  	_ = c.conn.SetReadDeadline(time.Now().Add(pongWait))
   115  	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
   116  
   117  	for {
   118  		select {
   119  		case <-c.ctx.Done():
   120  			return
   121  		default:
   122  			mType, buff, err := c.conn.ReadMessage()
   123  			if err != nil {
   124  				logger.Infof("[ws.read] readPump ReadMessage err:%V", err)
   125  				c.cancel()
   126  				return
   127  			}
   128  			if mType == websocket.PingMessage {
   129  				continue
   130  			}
   131  			if len(buff) == 0 {
   132  				continue
   133  			}
   134  			logger.Debugf("[ws.read] reader message:%v", string(buff))
   135  			if string(buff) == "ping" {
   136  				_ = c.AsyncSend(context.Background(), 0, []byte("pong"))
   137  			} else {
   138  				// 构建当前客户端请求的request数据
   139  				req, err := NewRequest(c, buff)
   140  				if err != nil {
   141  					logger.Warnf("[ws.read] data format err:%v", err)
   142  					return
   143  				}
   144  				c.server.handler.AsyncExecute(req)
   145  			}
   146  		}
   147  	}
   148  }
   149  
   150  // Start 启动连接,让当前连接开始工作
   151  func (c *wsConnection) Start() {
   152  	c.ctx, c.cancel = context.WithCancel(context.Background())
   153  
   154  	if c.server.Options().OnConnStart != nil {
   155  		c.server.Options().OnConnStart(c)
   156  	}
   157  
   158  	// 开启用户从客户端读取数据的Goroutine
   159  	go c.startReader()
   160  	// 开启用于写回客户端数据的Goroutine
   161  	go c.startWriter()
   162  }
   163  
   164  // Stop 关闭连接
   165  func (c *wsConnection) Stop() {
   166  	c.mu.Lock()
   167  	defer c.mu.Unlock()
   168  
   169  	//如果当前链接已经关闭
   170  	if c.isClosed == true {
   171  		return
   172  	}
   173  
   174  	//如果用户注册了该连接的关闭回调业务,那么在此调用
   175  	if c.server.Options().OnConnStop != nil {
   176  		c.server.Options().OnConnStop(c)
   177  	}
   178  
   179  	//关闭socket连接
   180  	if err := c.conn.Close(); err != nil {
   181  		logger.Warnf("[ws.stop] connection closed err:%v", err)
   182  	}
   183  
   184  	//关闭Writer
   185  	c.cancel()
   186  	// 将连接从连接管理器中删除
   187  	c.server.GetManager(c.id).Remove(c)
   188  	// 关闭该连接全部管道
   189  	close(c.msgChan)
   190  	//设置标志位
   191  	c.isClosed = true
   192  }
   193  
   194  func (c *wsConnection) Context() context.Context {
   195  	return c.ctx
   196  }
   197  
   198  // GetID 获取连接id
   199  func (c *wsConnection) GetID() uint64 {
   200  	return c.id
   201  }
   202  
   203  // GetUID 获取连接id
   204  func (c *wsConnection) GetUID() int {
   205  	return c.uid
   206  }
   207  
   208  // RemoteAddr 获取远程客户端地址信息
   209  func (c *wsConnection) RemoteAddr() net.Addr {
   210  	return c.conn.RemoteAddr()
   211  }
   212  
   213  // Send 发送数据给远程的WS客户端
   214  func (c *wsConnection) Send(ctx context.Context, mid int, msg []byte) error {
   215  	if c.isClosed == true {
   216  		return ErrConnNotFinish
   217  	}
   218  
   219  	//写回客户端
   220  	if err := c.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
   221  		return err
   222  	}
   223  	return nil
   224  }
   225  
   226  // AsyncSend 异步发送数据给远程的WS客户端
   227  func (c *wsConnection) AsyncSend(ctx context.Context, mid int, msg []byte) error {
   228  	if c.isClosed == true {
   229  		return ErrConnNotFinish
   230  	}
   231  
   232  	//写回客户端
   233  	c.msgChan <- msg
   234  	return nil
   235  }