加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
session.go 3.27 KB
一键复制 编辑 原始数据 按行查看 历史
周睿 提交于 2021-01-21 15:26 . 优化报错
package socket_server
import (
"bytes"
"context"
"fmt"
"github.com/ZR233/goutils/stackx"
"net"
"sync"
"time"
)
type Session struct {
id uint32
server *Server
net.Conn
cmdChan chan CmdInterface
ctxCmd context.Context
cancelCmd context.CancelFunc
ctxRead context.Context
cancelRead context.CancelFunc
readBuff []byte
Fields interface{}
closeOnce sync.Once
}
func (s *Session) close(err error) {
s.closeOnce.Do(func() {
s.server.options.Handler.OnClose(s, err)
s.Fields = nil
s.server.deleteSession(s.id)
s.cancelCmd()
s.cancelRead()
_ = s.Conn.Close()
close(s.cmdChan)
})
return
}
func (s *Session) Close() (err error) {
s.close(nil)
return nil
}
func (s *Session) CloseWithError(err error) error {
s.close(err)
return nil
}
func (s *Session) NewCmdClose(err error) *CmdClose {
return &CmdClose{
session: s,
err: err,
}
}
func newSession(id uint32, server *Server, conn net.Conn) (s *Session) {
s = &Session{
id: id,
server: server,
Conn: conn,
cmdChan: make(chan CmdInterface, 10),
}
s.readBuff = make([]byte, s.server.options.BuffLen)
s.ctxCmd, s.cancelCmd = context.WithCancel(s.server.ctxCmd)
s.ctxRead, s.cancelRead = context.WithCancel(s.server.ctxCmd)
s.server.options.Handler.OnConnect(s)
go s.runReadLoop()
go s.runCmdLoop()
return
}
func (s *Session) runReadLoop() {
for {
if s.ctxRead.Err() != nil {
return
}
s.readLoop()
}
}
func (s *Session) runCmdLoop() {
for {
select {
case <-s.ctxCmd.Done():
return
case cmd, ok := <-s.cmdChan:
if ok {
cmd.Exec()
}
}
}
}
func (s *Session) readLoop() {
defer func() {
if p := recover(); p != nil {
err := fmt.Errorf("%s\n%s", p, string(stackx.Stack(0)))
s.handleError(err)
}
}()
handler := s.server.options.Handler
headerData, err := s.read(handler.HeaderLen())
if err != nil {
return
}
thisRecv := context.Background()
bodyLen, thisRecvUpdated, err := handler.HeaderHandler(headerData, s, thisRecv)
if err != nil {
panic(err)
}
bodyData, err := s.read(bodyLen)
if err != nil {
return
}
err = handler.BodyHandler(bodyData, s, thisRecvUpdated)
if err != nil {
panic(err)
}
return
}
func (s *Session) read(wantLen int) (data []byte, err error) {
deadline := time.Now().Add(s.server.options.ReadTimeout)
if s.server.options.ReadTimeout == 0 {
deadline = time.Time{}
}
err = s.SetReadDeadline(deadline)
if err != nil {
s.close(err)
return
}
buf := bytes.NewBuffer(data)
n := 0
for {
if buf.Len() == wantLen {
break
}
buffLen := wantLen - buf.Len()
if buffLen > s.server.options.BuffLen {
buffLen = s.server.options.BuffLen
}
dataBatch := s.readBuff[:buffLen]
n, err = s.Read(dataBatch)
if err != nil {
s.close(err)
return
}
buf.Write(dataBatch[:n])
}
return buf.Bytes(), nil
}
func (s *Session) handleError(err error) {
if s.ctxCmd.Err() == nil {
s.server.handleError(err, s)
}
}
func (s *Session) NewCommandWrite(data []byte) *CmdWrite {
return &CmdWrite{
session: s,
data: data,
}
}
func (s *Session) ExecCommands(commands ...CmdInterface) {
defer func() {
recover()
}()
for _, command := range commands {
s.cmdChan <- command
}
}
func (s *Session) Id() uint32 {
return s.id
}
func (s *Session) Stopped() bool {
return s.ctxCmd.Err() != nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化