gouxp

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2020 License: MIT Imports: 18 Imported by: 0

README

gouxp

Powered

基于gokcp开箱即用的可靠UDP传输协议开发包。

如何使用

请参考sample/template目录下client和server代码示例。

技术特性

1. gouxp托管原始PacketConn对象

无论客户端或服务端,在创建PacketConn对象后交由gouxp托管,在托管之前PacketConn可按照自有方式进行收发,托管之后的收发以及关闭均由gouxp控制。

2. 以回调方式将数据返回用户层(应用层)

用户层对于gouxp的交互方式为接口回调(参见interface.go),之所以采用回调,主要考虑是简单且减少与gouxp不必要的交互。用户只需要关注PacketConn关闭了(OnClosed)、有数据来了(OnNewDataComing)这两个事件即可。

3. 读写分离

PacketConn的读写与KCP的读写由两个goroutinue负责,PacketConn的读写阻塞不影响KCP的读写。

4. 内置完整Chacha20poly1305和Salas20加解密

使用Chacha20ploy1305和Salas20算法对数据进行加解密,握手阶段交互双方密钥。gouxp数据包中预留了数据校验mac,默认的mac空位放在数据包头,但ChaCha20poly1305的校验mac是放在数据包末尾,因此使用Chacha20ploy1305加密时,会预先将预留的mac空位移动到末尾,会有额外一次copy的开销,而Salas20的校验mac空位是放在数据包头,对性能敏感的地方需要谨慎考虑。

5. FEC支持

gouxp支持FEC(前向纠错),在公网上(典型场景如移动网络)减少包重传。

接口

NewServer(rwc net.PacketConn, handler ServerHandler, parallelCount uint32) *Server

新建一个Server,rwc通过net.ListenUDP产生,handler为事件回调,parallelCount为执行所有ServerConn kcp.Update的goroutine数目,过小可能会导致CPU占用偏高,推荐值2、4、6。

func (s *Server) UseCryptoCodec(cryptoType CryptoType)

Server端使用何种加解密方式。

func (s *Server) Close()

手动关闭Server,此函数将会关闭所有服务端连接,不可重用。

func (s *Server) Start()

Server端开始工作,进入UDP读状态。

NewClientConn(rwc net.PacketConn, addr net.Addr, handler ConnHandler) *ClientConn

新建一个Client,rwc通过net.ListenUDP产生,addr为远端地址,handler为事件回调。

func (conn *ClientConn) UseCryptoCodec(cryptoType CryptoType)

Client端使用何种加解密方式。

func (conn *ClientConn) Start() error

Client端开始工作,按照gouxp工作流程,会先发送握手数据包,等待Server端的握手回包,交换加解密公钥,此后Server端和Client端开始正常的业务通信。

func (conn *RawConn) EnableFEC()

开启FEC。

func (conn *RawConn) ID() uint32

返回当前链接对应的会话ID。

func (conn *RawConn) SetWindow(sndWnd, rcvWnd int)

设置发送窗口大小和接收窗口大小,可以简单理解为TCP的SND_BUF和RCV_BUF,这里的单位是个数,默认为32,建议以32的倍数扩增。

func (conn *RawConn) SetMTU(mtu int) bool

设置传输路径MTU。

func (conn *RawConn) SetUpdateInterval(interval int)

设置KCP状态循环间隔,推荐值为5ms、10ms、15ms。

func (conn *RawConn) IsClosed() bool

链接是否已经关闭。

func (conn *RawConn) Close()

手动关闭链接。

func (conn *RawConn) Write(data []byte) (int, error)

向远端发送数据,可能返回的错误有ErrDataLenInvalid和ErrTryAgain,前者可能data长度非法,后者是因为等待发送的数据包过多。

func (conn *RawConn) StartKCPStatus()

KCP状态输出,需要向gouxp注入Logger对象,以5秒定时向日志输出当前Conn对应的KCP状态,方便调试,后面会以HTTP方式提供此调试服务。

func (conn *RawConn) StopKCPStatus()

停止KCP状态输出。

func SetDebugLogger(l Logger)

向gouxp注入Logger对象。

Q&A

  1. 单次最大发送数据是多少?
    对于使用UDP传输协议而言,单次传输的数据应尽量不要超过网络路径MTU,但也不应过低。在gouxp中,用户逻辑数据最大大小(KCP.MTU() - PacketHeaderSize - KCPHeader - FECHeader)。默认情况下,PacketHeaderSize长度为18,其中为16字节的mac,2字节的协议类型;KCPHeader为24字节,FECHeader为8字节,其中前4个字节为FEC数据包序号,2个字节为FEC数据包类型,最后2个字节为上层数据包长度。

  2. 由于UDP面向无连接,如何模拟TCP的连接与断开方便应用层逻辑上的接入?
    首先,限与UDP的特性,无法准确感知UDP的连接与断开,所以在调用ClientConn.Start时,会向服务端发送握手协议,服务端回发握手协议并交换双方公钥,此过程结束之后代表双方可以开始正常通信。其次,ClientConn与ServerConn均使用了心跳检测机制,客户端在握手成功之后,每3秒会向服务端发送心跳数据包,心跳检测周期为3秒,两端均可在心跳过期之后“关闭”连接。

参考

Documentation

Index

Constants

View Source
const (
	FECDataShards   = 3
	FECParityShards = 2
)
View Source
const (
	PacketHeaderSize uint16 = macSize + protoSize
)

packet protocol: raw data -> kcp data -> [compress] -> crypto -> fec

Variables

View Source
var (
	InitCryptoKey   = []byte("0053A6F94C9FF24598EB3E91E4378ADD")
	InitCryptoNonce = []byte("0D74DB42A91077DEB3E91E43")
)
View Source
var (
	ErrConnClosed          = errors.New("connection is closed")
	ErrDifferentAddr       = errors.New("different remote addr.")
	ErrMessageAuthFailed   = errors.New("message authentication failed")
	ErrHeartbeatTimeout    = errors.New("conn heartbeat timeout")
	ErrInvalidNonceSize    = errors.New("invalid nonce size")
	ErrTryAgain            = errors.New("try again")
	ErrWriteDataTooLong    = errors.New("write data too long")
	ErrUnknownProtocolType = errors.New("unknown protocol type")
	ErrExistConnection     = errors.New("exist conneciton")
)
View Source
var (
	ErrUnknownFecCmd  = errors.New("unknown fec cmd")
	ErrFecDataTimeout = errors.New("fec data timeout")
	ErrNoFecData      = errors.New("no fec data")
)
View Source
var ConvID uint32 = 555

Functions

func SetDebugLogger

func SetDebugLogger(l Logger)

func SetMaxDataLengthLimit

func SetMaxDataLengthLimit(n int)

Types

type Chacha20poly1305Crypto

type Chacha20poly1305Crypto struct {
	// contains filtered or unexported fields
}

func NewChacha20poly1305CryptoCodec

func NewChacha20poly1305CryptoCodec() *Chacha20poly1305Crypto

In chacha20poly1305, data format is |---DATA---|---MAC---| In gouxp, data format is |---MAC---|---DATA---| When use chacha20poly1305, must modify data format to adapt chacha20poly1305. Chacha20poly1305 use 12bytes nonce In other way, you can use poly1305 + salsa20/chacha20 to avoid move origin data

func (*Chacha20poly1305Crypto) Decrypt

func (codec *Chacha20poly1305Crypto) Decrypt(src []byte) (dst []byte, err error)

func (*Chacha20poly1305Crypto) Encrypt

func (codec *Chacha20poly1305Crypto) Encrypt(src []byte) (dst []byte, err error)

change data format |---MAC---|---DATA---| to |---DATA---|---MAC---|

func (*Chacha20poly1305Crypto) SetKey

func (codec *Chacha20poly1305Crypto) SetKey(key []byte)

func (*Chacha20poly1305Crypto) SetReadNonce

func (codec *Chacha20poly1305Crypto) SetReadNonce(nonce []byte)

func (*Chacha20poly1305Crypto) SetWriteNonce

func (codec *Chacha20poly1305Crypto) SetWriteNonce(nonce []byte)

type ClientConn

type ClientConn struct {
	RawConn
	// contains filtered or unexported fields
}

func NewClientConn

func NewClientConn(rwc net.PacketConn, addr net.Addr, handler ConnHandler) *ClientConn

func (*ClientConn) Start

func (conn *ClientConn) Start() error

func (*ClientConn) UseCryptoCodec

func (conn *ClientConn) UseCryptoCodec(cryptoType CryptoType)

ClientConn

type ConnHandler

type ConnHandler interface {
	OnClosed(err error)
	OnNewDataComing(data []byte)
	OnReady()
}

type CryptCodec

type CryptCodec interface {
	Encrypt(src []byte) (dst []byte, err error)
	Decrypt(src []byte) (dst []byte, err error)
	SetKey(key []byte)
	SetReadNonce(nonce []byte)
	SetWriteNonce(nonce []byte)
}

type CryptoKeys

type CryptoKeys struct {
	// contains filtered or unexported fields
}

type CryptoNonce

type CryptoNonce struct {
	// contains filtered or unexported fields
}

type CryptoType

type CryptoType byte
const (
	UseChacha20 CryptoType = 0x05
	UseSalsa20  CryptoType = 0x06
)

type DataShards

type DataShards struct {
	// contains filtered or unexported fields
}

type FecCodecDecoder

type FecCodecDecoder struct {
	// contains filtered or unexported fields
}

func NewFecDecoder

func NewFecDecoder(dataShards, parityShards, bufferSize int) *FecCodecDecoder

func (*FecCodecDecoder) Decode

func (f *FecCodecDecoder) Decode(fecData []byte, now uint32) (rawData [][]byte, err error)

type FecCodecEncoder

type FecCodecEncoder struct {
	// contains filtered or unexported fields
}

func NewFecEncoder

func NewFecEncoder(dataShards, parityShards, bufferSize int) *FecCodecEncoder

func (*FecCodecEncoder) Encode

func (f *FecCodecEncoder) Encode(rawData []byte) (fecData [][]byte, err error)

type Logger

type Logger interface {
	Fatal(str string)
	Fatalf(format string, v ...interface{})
	Error(str string)
	Errorf(format string, v ...interface{})
	Warn(str string)
	Warnf(format string, v ...interface{})
	Info(str string)
	Infof(format string, v ...interface{})
	Debug(str string)
	Debugf(format string, v ...interface{})
}

type PlaintextData

type PlaintextData []byte

func (PlaintextData) Data

func (p PlaintextData) Data() []byte

func (PlaintextData) Type

func (p PlaintextData) Type() ProtoType

type ProtoType

type ProtoType uint16

type RawConn

type RawConn struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func (*RawConn) Close

func (conn *RawConn) Close()

func (*RawConn) EnableFEC

func (conn *RawConn) EnableFEC()

RawConn

func (*RawConn) ID

func (conn *RawConn) ID() uint32

func (*RawConn) IsClosed

func (conn *RawConn) IsClosed() bool

func (*RawConn) SetConnHandler

func (conn *RawConn) SetConnHandler(handler ConnHandler)

func (*RawConn) SetMTU

func (conn *RawConn) SetMTU(mtu int) bool

MUST invoke before start

func (*RawConn) SetUpdateInterval

func (conn *RawConn) SetUpdateInterval(interval int)

MUST invoke before start

func (*RawConn) SetWindow

func (conn *RawConn) SetWindow(sndWnd, rcvWnd int)

MUST invoke before start

func (*RawConn) StartKCPStatus

func (conn *RawConn) StartKCPStatus()

For use KCP status: Need To inject Logger object into gouxp

func (*RawConn) StopKCPStatus

func (conn *RawConn) StopKCPStatus()

func (*RawConn) Write

func (conn *RawConn) Write(data []byte) (int, error)

type Salsa20Crypto

type Salsa20Crypto struct {
	// contains filtered or unexported fields
}

func NewSalsa20CryptoCodec

func NewSalsa20CryptoCodec() *Salsa20Crypto

Salsa20 use 8 or 24bytes nonce, we choose 8bytes

func (*Salsa20Crypto) Decrypt

func (codec *Salsa20Crypto) Decrypt(src []byte) (dst []byte, err error)

func (*Salsa20Crypto) Encrypt

func (codec *Salsa20Crypto) Encrypt(src []byte) (dst []byte, err error)

func (*Salsa20Crypto) SetKey

func (codec *Salsa20Crypto) SetKey(key []byte)

func (*Salsa20Crypto) SetReadNonce

func (codec *Salsa20Crypto) SetReadNonce(nonce []byte)

func (*Salsa20Crypto) SetWriteNonce

func (codec *Salsa20Crypto) SetWriteNonce(nonce []byte)

type Server

type Server struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func NewServer

func NewServer(rwc net.PacketConn, handler ServerHandler, parallelCount uint32) *Server

func (*Server) Close

func (s *Server) Close()

user shuts down manually

func (*Server) Start

func (s *Server) Start()

func (*Server) UseCryptoCodec

func (s *Server) UseCryptoCodec(cryptoType CryptoType)

type ServerConn

type ServerConn struct {
	RawConn
	// contains filtered or unexported fields
}

type ServerHandler

type ServerHandler interface {
	OnNewConnComing(conn *ServerConn)
	OnConnClosed(conn *ServerConn, err error)
	OnClosed(err error)
}

type Task

type Task struct {
	// contains filtered or unexported fields
}

type TaskList

type TaskList []*Task

func (TaskList) Len

func (t TaskList) Len() int

func (TaskList) Less

func (t TaskList) Less(i int, j int) bool

func (*TaskList) Pop

func (t *TaskList) Pop() interface{}

func (*TaskList) Push

func (t *TaskList) Push(v interface{})

func (TaskList) Swap

func (t TaskList) Swap(i int, j int)

type TimerScheduler

type TimerScheduler struct {
	// contains filtered or unexported fields
}

func NewTimerScheduler

func NewTimerScheduler(parallelCount uint32) *TimerScheduler

func (*TimerScheduler) Close

func (ts *TimerScheduler) Close()

func (*TimerScheduler) PushTask

func (ts *TimerScheduler) PushTask(exec func(), t uint32)

Directories

Path Synopsis
sample

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL