wip: 又又加了一些新东西。
							parent
							
								
									6ec0070ebe
								
							
						
					
					
						commit
						115166cb11
					
				@ -0,0 +1,18 @@
 | 
			
		||||
package message
 | 
			
		||||
 | 
			
		||||
type BinarySerializer struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewBinarySerializer() Serializer {
 | 
			
		||||
	return &BinarySerializer{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *BinarySerializer) Marshal(i interface{}) ([]byte, error) {
 | 
			
		||||
	//TODO implement me
 | 
			
		||||
	panic("implement me")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *BinarySerializer) Unmarshal(bytes []byte, i interface{}) error {
 | 
			
		||||
	//TODO implement me
 | 
			
		||||
	panic("implement me")
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,29 @@
 | 
			
		||||
package nface
 | 
			
		||||
 | 
			
		||||
import "net"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// StatusStart 开始阶段
 | 
			
		||||
	StatusStart int32 = iota + 1
 | 
			
		||||
	// StatusPrepare 准备阶段
 | 
			
		||||
	StatusPrepare
 | 
			
		||||
	// StatusWorking 工作阶段
 | 
			
		||||
	StatusWorking
 | 
			
		||||
	// StatusClosed 连接关闭
 | 
			
		||||
	StatusClosed
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IConnection interface {
 | 
			
		||||
	// Status 获取连接状态
 | 
			
		||||
	Status() int32
 | 
			
		||||
	// SetStatus 设置连接状态
 | 
			
		||||
	SetStatus(s int32)
 | 
			
		||||
	// Conn 获取底层网络连接
 | 
			
		||||
	Conn() net.Conn
 | 
			
		||||
	// ID 获取连接ID
 | 
			
		||||
	ID() int64
 | 
			
		||||
	// Session 获取当前连接绑定的Session
 | 
			
		||||
	Session() ISession
 | 
			
		||||
	// Close 关闭连接
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,164 @@
 | 
			
		||||
package nnet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/log"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/nface"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/packet"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/pipeline"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/session"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrCloseClosedSession = errors.New("close closed session")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	Connection struct {
 | 
			
		||||
		session nface.ISession // Session
 | 
			
		||||
		server  *Server        // Server 引用
 | 
			
		||||
 | 
			
		||||
		conn            net.Conn // low-level conn fd
 | 
			
		||||
		status          int32    // 连接状态
 | 
			
		||||
		lastMid         uint64   // 最近一次消息ID
 | 
			
		||||
		lastHeartbeatAt int64    // 最近一次心跳时间
 | 
			
		||||
 | 
			
		||||
		chDie  chan struct{} // 停止通道
 | 
			
		||||
		chSend chan []byte   // 消息发送通道
 | 
			
		||||
 | 
			
		||||
		pipeline pipeline.Pipeline // 消息管道
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pendingMessage struct {
 | 
			
		||||
		typ     byte        // message type
 | 
			
		||||
		route   string      // message route
 | 
			
		||||
		mid     uint64      // response message id
 | 
			
		||||
		payload interface{} // payload
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newConnection(server *Server, conn net.Conn, pipeline pipeline.Pipeline) nface.IConnection {
 | 
			
		||||
	r := &Connection{
 | 
			
		||||
		conn:   conn,
 | 
			
		||||
		server: server,
 | 
			
		||||
		status: nface.StatusStart,
 | 
			
		||||
 | 
			
		||||
		lastHeartbeatAt: time.Now().Unix(),
 | 
			
		||||
 | 
			
		||||
		chDie:  make(chan struct{}),
 | 
			
		||||
		chSend: make(chan pendingMessage, 2048),
 | 
			
		||||
 | 
			
		||||
		pipeline: pipeline,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// binding session
 | 
			
		||||
	r.session = session.New()
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newConnectionWS(server *Server, conn *websocket.Conn, pipeline pipeline.Pipeline) nface.IConnection {
 | 
			
		||||
	c, err := newWSConn(conn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// TODO panic ?
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return newConnection(server, c, pipeline)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) Status() int32 {
 | 
			
		||||
	return atomic.LoadInt32(&r.status)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) SetStatus(s int32) {
 | 
			
		||||
	atomic.StoreInt32(&r.status, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) Conn() net.Conn {
 | 
			
		||||
	return r.conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) ID() int64 {
 | 
			
		||||
	return r.session.ID()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) Session() nface.ISession {
 | 
			
		||||
	return r.session
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) write() {
 | 
			
		||||
	ticker := time.NewTicker(r.server.HeartbeatInterval)
 | 
			
		||||
 | 
			
		||||
	chWrite := make(chan []byte, 1024)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		ticker.Stop()
 | 
			
		||||
		close(r.chSend)
 | 
			
		||||
		close(chWrite)
 | 
			
		||||
		_ = r.Close()
 | 
			
		||||
 | 
			
		||||
		log.Debugf("Session write goroutine exit, SessionID=%d, UID=%d", r.session.ID(), r.session.UID())
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			deadline := time.Now().Add(-2 * r.server.HeartbeatInterval).Unix()
 | 
			
		||||
			if atomic.LoadInt64(&r.lastHeartbeatAt) < deadline {
 | 
			
		||||
				log.Debugf("Session heartbeat timeout, LastTime=%d, Deadline=%d", atomic.LoadInt64(&r.lastHeartbeatAt), deadline)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			// TODO heartbeat data
 | 
			
		||||
			chWrite <- []byte{}
 | 
			
		||||
		case data := <-r.chSend:
 | 
			
		||||
			// message marshal data
 | 
			
		||||
			payload, err := r.server.Serializer.Marshal(data.payload)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				switch data.typ {
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			// TODO new message and pipeline
 | 
			
		||||
 | 
			
		||||
			// TODO encode message ? message processor ?
 | 
			
		||||
 | 
			
		||||
			// packet pack data
 | 
			
		||||
			p, err := r.server.Packer.Pack(packet.Data, payload)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error(err.Error())
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			chWrite <- p
 | 
			
		||||
		case data := <-chWrite:
 | 
			
		||||
			// 回写数据
 | 
			
		||||
			if _, err := r.conn.Write(data); err != nil {
 | 
			
		||||
				log.Error(err.Error())
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		case <-r.chDie: // connection close signal
 | 
			
		||||
			return
 | 
			
		||||
			// TODO application quit signal
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Connection) Close() error {
 | 
			
		||||
	if r.Status() == StatusClosed {
 | 
			
		||||
		return ErrCloseClosedSession
 | 
			
		||||
	}
 | 
			
		||||
	r.SetStatus(StatusClosed)
 | 
			
		||||
 | 
			
		||||
	log.Debugf("close connection, ID: %d", r.ID())
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case <-r.chDie:
 | 
			
		||||
	default:
 | 
			
		||||
		close(r.chDie)
 | 
			
		||||
		// TODO lifetime
 | 
			
		||||
	}
 | 
			
		||||
	return r.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
package nnet
 | 
			
		||||
@ -1,63 +0,0 @@
 | 
			
		||||
package nnet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/nface"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/pipeline"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/session"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Request struct {
 | 
			
		||||
	session nface.ISession // Session
 | 
			
		||||
 | 
			
		||||
	conn            net.Conn // low-level conn fd
 | 
			
		||||
	status          Status   // 连接状态
 | 
			
		||||
	lastMid         uint64   // 最近一次消息ID
 | 
			
		||||
	lastHeartbeatAt int64    // 最近一次心跳时间
 | 
			
		||||
 | 
			
		||||
	chDie  chan struct{} // 停止通道
 | 
			
		||||
	chSend chan []byte   // 消息发送通道
 | 
			
		||||
 | 
			
		||||
	pipeline pipeline.Pipeline // 消息管道
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRequest(conn net.Conn, pipeline pipeline.Pipeline) *Request {
 | 
			
		||||
	r := &Request{
 | 
			
		||||
		conn:   conn,
 | 
			
		||||
		status: StatusStart,
 | 
			
		||||
 | 
			
		||||
		lastHeartbeatAt: time.Now().Unix(),
 | 
			
		||||
 | 
			
		||||
		chDie:  make(chan struct{}),
 | 
			
		||||
		chSend: make(chan []byte),
 | 
			
		||||
 | 
			
		||||
		pipeline: pipeline,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// binding session
 | 
			
		||||
	r.session = session.New()
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRequestWS(conn *websocket.Conn, pipeline pipeline.Pipeline) *Request {
 | 
			
		||||
	c, err := newWSConn(conn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// TODO panic ?
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return newRequest(c, pipeline)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Request) Status() Status {
 | 
			
		||||
	return r.status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Request) ID() int64 {
 | 
			
		||||
	return r.session.ID()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Request) Session() nface.ISession {
 | 
			
		||||
	return r.session
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
package nnet
 | 
			
		||||
 | 
			
		||||
type Status uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// StatusStart 开始阶段
 | 
			
		||||
	StatusStart Status = iota + 1
 | 
			
		||||
	// StatusPrepare 准备阶段
 | 
			
		||||
	StatusPrepare
 | 
			
		||||
	// StatusWorking 工作阶段
 | 
			
		||||
	StatusWorking
 | 
			
		||||
	// StatusClosed 连接关闭
 | 
			
		||||
	StatusClosed
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package packet
 | 
			
		||||
 | 
			
		||||
import "git.noahlan.cn/northlan/nnet/nface"
 | 
			
		||||
 | 
			
		||||
// Type 数据帧类型,如:握手,心跳,数据等
 | 
			
		||||
type Type byte
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	Packer interface {
 | 
			
		||||
		// Pack 从原始raw bytes创建一个用于网络传输的 packet.Packet 结构
 | 
			
		||||
		Pack(typ Type, data []byte) ([]byte, error)
 | 
			
		||||
 | 
			
		||||
		// Unpack 解包
 | 
			
		||||
		Unpack(data []byte) ([]interface{}, error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Processor 数据帧处理器,拆包之后的处理
 | 
			
		||||
	Processor interface {
 | 
			
		||||
		// ProcessPacket 单个数据包处理方法
 | 
			
		||||
		// packet 为实际数据包,是 packet.Packer 的Unpack方法拆包出来的数据指针
 | 
			
		||||
		ProcessPacket(conn nface.IConnection, packet interface{}) error
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
package packet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/log"
 | 
			
		||||
	"git.noahlan.cn/northlan/nnet/nface"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DefaultProcessor struct{}
 | 
			
		||||
 | 
			
		||||
func NewDefaultProcessor() *DefaultProcessor {
 | 
			
		||||
	return &DefaultProcessor{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DefaultProcessor) ProcessPacket(conn nface.IConnection, packet interface{}) error {
 | 
			
		||||
	p := packet.(*Packet)
 | 
			
		||||
	switch p.Type {
 | 
			
		||||
	case Handshake:
 | 
			
		||||
		// TODO validate handshake
 | 
			
		||||
		if _, err := conn.Conn().Write([]byte{}); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		conn.SetStatus(nface.StatusPrepare)
 | 
			
		||||
		log.Debugf("Connection handshake Id=%d, Remote=%s", conn.ID(), conn.Conn().RemoteAddr())
 | 
			
		||||
	case HandshakeAck:
 | 
			
		||||
		conn.SetStatus(nface.StatusWorking)
 | 
			
		||||
		log.Debugf("Receive handshake ACK Id=%d, Remote=%s", conn.ID(), conn.Conn().RemoteAddr())
 | 
			
		||||
 | 
			
		||||
	case Data:
 | 
			
		||||
		if conn.Status() < nface.StatusWorking {
 | 
			
		||||
			return fmt.Errorf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
 | 
			
		||||
				conn.Conn().RemoteAddr())
 | 
			
		||||
		}
 | 
			
		||||
		// TODO message data 处理
 | 
			
		||||
	case Heartbeat:
 | 
			
		||||
		// expected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue