From ead780dff4633a2e7f6df09cdc2fdb50b271b78f Mon Sep 17 00:00:00 2001 From: godotg Date: Wed, 14 Sep 2022 16:29:17 +0800 Subject: [PATCH] =?UTF-8?q?feat[go]:=20=E6=94=AF=E6=8C=81go=E7=9A=84?= =?UTF-8?q?=E5=8D=95=E6=9C=BA=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/idea/go-set-up.md | 17 +++ net/src/test/go/base/base.go | 134 +++++++++++++++++++ net/src/test/go/go.mod | 3 + net/src/test/go/gonet.go | 30 +++++ net/src/test/go/net/codec.go | 83 ++++++++++++ net/src/test/go/net/codec_test.go | 36 +++++ net/src/test/go/net/conn.go | 166 +++++++++++++++++++++++ net/src/test/go/net/def.go | 29 ++++ net/src/test/go/net/message.go | 78 +++++++++++ net/src/test/go/net/service.go | 198 ++++++++++++++++++++++++++++ net/src/test/go/net/service_test.go | 79 +++++++++++ net/src/test/go/net/session.go | 73 ++++++++++ net/src/test/go/net/uuid.go | 64 +++++++++ 13 files changed, 990 insertions(+) create mode 100644 doc/idea/go-set-up.md create mode 100644 net/src/test/go/base/base.go create mode 100644 net/src/test/go/go.mod create mode 100644 net/src/test/go/gonet.go create mode 100644 net/src/test/go/net/codec.go create mode 100644 net/src/test/go/net/codec_test.go create mode 100644 net/src/test/go/net/conn.go create mode 100644 net/src/test/go/net/def.go create mode 100644 net/src/test/go/net/message.go create mode 100644 net/src/test/go/net/service.go create mode 100644 net/src/test/go/net/service_test.go create mode 100644 net/src/test/go/net/session.go create mode 100644 net/src/test/go/net/uuid.go diff --git a/doc/idea/go-set-up.md b/doc/idea/go-set-up.md new file mode 100644 index 00000000..784cb682 --- /dev/null +++ b/doc/idea/go-set-up.md @@ -0,0 +1,17 @@ +## 1. go安装,windows + +- 下载地址:https://golang.google.cn/dl/,这里提供了不同平台的go版本,根据自己的平台选择下载 + +- 安装完成后,把C:\go\bin目录添加到环境变量,这里就可以使用go了,在命令行输入 go version查看版本 + +``` +最新版本得会自动添加path +``` + +## 常用命令 + +- go run xx.go 运行go程序 + +- go env 打印go得环境变量 + +- go get mysql 下载包 diff --git a/net/src/test/go/base/base.go b/net/src/test/go/base/base.go new file mode 100644 index 00000000..9b3ec3eb --- /dev/null +++ b/net/src/test/go/base/base.go @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package base + +import ( + "fmt" + "time" +) + +func init() { + fmt.Println("base init") +} + +func VarTest() { + var a string = "Runoob" + fmt.Println(a) + + var b, c int = 1, 2 + fmt.Println(b, c) +} + +func NilTest() { + var a *int + var b []int + var c map[string]int + var d chan int + var e func(string) int + var f error // error 是接口 + + fmt.Println(a) + fmt.Println(b) + fmt.Println(c) + fmt.Println(d) + fmt.Println(e) + fmt.Println(f) +} + +func ConstTest() { + const LENGTH int = 10 + const WIDTH int = 5 + var area int + const a, b, c = 1, false, "str" //多重赋值 + + area = LENGTH * WIDTH + fmt.Printf("面积为 : %d", area) + println() + println(a, b, c) +} + +func IfTest() { + /* 局部变量定义 */ + var a int = 100 + + /* 判断布尔表达式 */ + if a < 20 { + /* 如果条件为 true 则执行以下语句 */ + fmt.Println("a 小于 20") + } else { + /* 如果条件为 false 则执行以下语句 */ + fmt.Println("a 不小于 20") + } + fmt.Println("a 的值为 : ", a) +} + +func ForTest() { + sum := 0 + for i := 0; i <= 10; i++ { + sum += i + } + fmt.Println(sum) + + // for each + var strArray = []string{"google", "runoob"} + for i, s := range strArray { + fmt.Println(i, s) + } + + // for map + map1 := make(map[int]float32) + map1[1] = 1.0 + map1[2] = 2.0 + map1[3] = 3.0 + map1[4] = 4.0 + + // 读取 key 和 value + for key, value := range map1 { + fmt.Printf("key is: %d - value is: %f\n", key, value) + } +} + +/* 函数返回两个数的最大值 */ +func maxTest(num1, num2 int) int { + /* 定义局部变量 */ + var result int + + if num1 > num2 { + result = num1 + } else { + result = num2 + } + return result +} + +func Max(num1, num2 int) int { + type maxFunc func(int, int) int + var max maxFunc + max = maxTest + return max(num1, num2) +} + +var myChan = make(chan string) + +func show(msg string) { + fmt.Println(msg) + time.Sleep(time.Millisecond * 5000) + myChan <- ("go" + msg) +} + +func RoutinesTest() { + go show("java") + fmt.Println("wait...") + var msg = <-myChan + fmt.Println(msg) +} diff --git a/net/src/test/go/go.mod b/net/src/test/go/go.mod new file mode 100644 index 00000000..f5c215b1 --- /dev/null +++ b/net/src/test/go/go.mod @@ -0,0 +1,3 @@ +module gonet + +go 1.19 diff --git a/net/src/test/go/gonet.go b/net/src/test/go/gonet.go new file mode 100644 index 00000000..cb3cdafe --- /dev/null +++ b/net/src/test/go/gonet.go @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package main + +import ( + "fmt" + "gonet/base" +) + +func main() { + fmt.Println("hello world") + + //base.VarTest() + //base.NilTest() + //base.ConstTest() + //base.IfTest() + //base.ForTest() + //fmt.Println(base.Max(1, 2)) + base.RoutinesTest() +} diff --git a/net/src/test/go/net/codec.go b/net/src/test/go/net/codec.go new file mode 100644 index 00000000..17d4dd27 --- /dev/null +++ b/net/src/test/go/net/codec.go @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package net + +import ( + "bytes" + "encoding/binary" + "errors" +) + +// Encode from Message to []byte +func Encode(msg *Message) ([]byte, error) { + buffer := new(bytes.Buffer) + + err := binary.Write(buffer, binary.LittleEndian, msg.msgSize) + if err != nil { + return nil, err + } + err = binary.Write(buffer, binary.LittleEndian, msg.msgID) + if err != nil { + return nil, err + } + err = binary.Write(buffer, binary.LittleEndian, msg.data) + if err != nil { + return nil, err + } + err = binary.Write(buffer, binary.LittleEndian, msg.checksum) + if err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} + +// Decode from []byte to Message +func Decode(data []byte) (*Message, error) { + bufReader := bytes.NewReader(data) + + dataSize := len(data) + // 读取消息ID + var msgID int32 + err := binary.Read(bufReader, binary.LittleEndian, &msgID) + if err != nil { + return nil, err + } + + // 读取数据 + dataBufLength := dataSize - 4 - 4 + dataBuf := make([]byte, dataBufLength) + err = binary.Read(bufReader, binary.LittleEndian, &dataBuf) + if err != nil { + return nil, err + } + + // 检查checksum + var checksum uint32 + err = binary.Read(bufReader, binary.LittleEndian, &checksum) + if err != nil { + return nil, err + } + + message := &Message{} + message.msgSize = int32(dataSize) + message.msgID = msgID + message.data = dataBuf + message.checksum = checksum + + if message.Verify() { + return message, nil + } + + return nil, errors.New("checksum error") +} diff --git a/net/src/test/go/net/codec_test.go b/net/src/test/go/net/codec_test.go new file mode 100644 index 00000000..5c6b0b72 --- /dev/null +++ b/net/src/test/go/net/codec_test.go @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package net + +import "testing" + +func TestCodec(t *testing.T) { + // test encode + msg1 := NewMessage(1, []byte("message codec test...")) + + data, err := Encode(msg1) + if err != nil { + t.Fatal(err) + } + + t.Log(msg1) + + // test decode + // The first four bytes is size for socket read + msg2, err := Decode(data[4:]) + if err != nil { + t.Fatal(err) + } + + t.Logf("ID=%d, Data=%s", msg2.GetID(), string(msg2.GetData())) +} diff --git a/net/src/test/go/net/conn.go b/net/src/test/go/net/conn.go new file mode 100644 index 00000000..96dc5342 --- /dev/null +++ b/net/src/test/go/net/conn.go @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package net + +import ( + "bytes" + "context" + "encoding/binary" + "io" + "net" + "time" +) + +// Conn wrap net.Conn +type Conn struct { + sid string + rawConn net.Conn + sendCh chan []byte + done chan error + hbTimer *time.Timer + name string + messageCh chan *Message + hbInterval time.Duration + hbTimeout time.Duration +} + +// GetName Get conn name +func (c *Conn) GetName() string { + return c.name +} + +// NewConn create new conn +func NewConn(c net.Conn, hbInterval time.Duration, hbTimeout time.Duration) *Conn { + conn := &Conn{ + rawConn: c, + sendCh: make(chan []byte, 100), + done: make(chan error), + messageCh: make(chan *Message, 100), + hbInterval: hbInterval, + hbTimeout: hbTimeout, + } + + conn.name = c.RemoteAddr().String() + conn.hbTimer = time.NewTimer(conn.hbInterval) + + if conn.hbInterval == 0 { + conn.hbTimer.Stop() + } + + return conn +} + +// Close close connection +func (c *Conn) Close() { + c.hbTimer.Stop() + c.rawConn.Close() +} + +// SendMessage send message +func (c *Conn) SendMessage(msg *Message) error { + pkg, err := Encode(msg) + if err != nil { + return err + } + + c.sendCh <- pkg + return nil +} + +// writeCoroutine write coroutine +func (c *Conn) writeCoroutine(ctx context.Context) { + hbData := make([]byte, 0) + + for { + select { + case <-ctx.Done(): + return + + case pkt := <-c.sendCh: + + if pkt == nil { + continue + } + + if _, err := c.rawConn.Write(pkt); err != nil { + c.done <- err + } + + case <-c.hbTimer.C: + hbMessage := NewMessage(MsgHeartbeat, hbData) + c.SendMessage(hbMessage) + // 设置心跳timer + if c.hbInterval > 0 { + c.hbTimer.Reset(c.hbInterval) + } + } + } +} + +// readCoroutine read coroutine +func (c *Conn) readCoroutine(ctx context.Context) { + + for { + select { + case <-ctx.Done(): + return + + default: + // 设置超时 + if c.hbInterval > 0 { + err := c.rawConn.SetReadDeadline(time.Now().Add(c.hbTimeout)) + if err != nil { + c.done <- err + continue + } + } + // 读取长度 + buf := make([]byte, 4) + _, err := io.ReadFull(c.rawConn, buf) + if err != nil { + c.done <- err + continue + } + + bufReader := bytes.NewReader(buf) + + var dataSize int32 + err = binary.Read(bufReader, binary.LittleEndian, &dataSize) + if err != nil { + c.done <- err + continue + } + + // 读取数据 + databuf := make([]byte, dataSize) + _, err = io.ReadFull(c.rawConn, databuf) + if err != nil { + c.done <- err + continue + } + + // 解码 + msg, err := Decode(databuf) + if err != nil { + c.done <- err + continue + } + + if msg.GetID() == MsgHeartbeat { + continue + } + + c.messageCh <- msg + } + } +} diff --git a/net/src/test/go/net/def.go b/net/src/test/go/net/def.go new file mode 100644 index 00000000..0775355b --- /dev/null +++ b/net/src/test/go/net/def.go @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package net + +const ( + // STUnknown Unknown + STUnknown = iota + // STInited Inited + STInited + // STRunning Running + STRunning + // STStop Stop + STStop +) + +const ( + // MsgHeartbeat heartbeat + MsgHeartbeat = iota +) diff --git a/net/src/test/go/net/message.go b/net/src/test/go/net/message.go new file mode 100644 index 00000000..a45a531b --- /dev/null +++ b/net/src/test/go/net/message.go @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package net + +import ( + "bytes" + "encoding/binary" + "fmt" + "hash/adler32" +) + +// Message struct +type Message struct { + msgSize int32 + msgID int32 + data []byte + checksum uint32 +} + +// NewMessage create a new message +func NewMessage(msgID int32, data []byte) *Message { + msg := &Message{ + msgSize: int32(len(data)) + 4 + 4, + msgID: msgID, + data: data, + } + + msg.checksum = msg.calcChecksum() + return msg +} + +// GetData get message data +func (msg *Message) GetData() []byte { + return msg.data +} + +// GetID get message ID +func (msg *Message) GetID() int32 { + return msg.msgID +} + +// Verify verify checksum +func (msg *Message) Verify() bool { + return msg.checksum == msg.calcChecksum() +} + +func (msg *Message) calcChecksum() uint32 { + if msg == nil { + return 0 + } + + data := new(bytes.Buffer) + + err := binary.Write(data, binary.LittleEndian, msg.msgID) + if err != nil { + return 0 + } + err = binary.Write(data, binary.LittleEndian, msg.data) + if err != nil { + return 0 + } + + checksum := adler32.Checksum(data.Bytes()) + return checksum +} + +func (msg *Message) String() string { + return fmt.Sprintf("Size=%d ID=%d DataLen=%d Checksum=%d", msg.msgSize, msg.GetID(), len(msg.GetData()), msg.checksum) +} diff --git a/net/src/test/go/net/service.go b/net/src/test/go/net/service.go new file mode 100644 index 00000000..e2b6e833 --- /dev/null +++ b/net/src/test/go/net/service.go @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package net + +import ( + "context" + "errors" + "net" + "sync" + "time" +) + +// SocketService struct +type SocketService struct { + onMessage func(*Session, *Message) + onConnect func(*Session) + onDisconnect func(*Session, error) + sessions *sync.Map + hbInterval time.Duration + hbTimeout time.Duration + laddr string + status int + listener net.Listener + stopCh chan error +} + +// NewSocketService create a new socket service +func NewSocketService(laddr string) (*SocketService, error) { + + l, err := net.Listen("tcp", laddr) + + if err != nil { + return nil, err + } + + s := &SocketService{ + sessions: &sync.Map{}, + stopCh: make(chan error), + hbInterval: 0 * time.Second, + hbTimeout: 0 * time.Second, + laddr: laddr, + status: STInited, + listener: l, + } + + return s, nil +} + +// RegMessageHandler register message handler +func (s *SocketService) RegMessageHandler(handler func(*Session, *Message)) { + s.onMessage = handler +} + +// RegConnectHandler register connect handler +func (s *SocketService) RegConnectHandler(handler func(*Session)) { + s.onConnect = handler +} + +// RegDisconnectHandler register disconnect handler +func (s *SocketService) RegDisconnectHandler(handler func(*Session, error)) { + s.onDisconnect = handler +} + +// Serv Start socket service +func (s *SocketService) Serv() { + + s.status = STRunning + ctx, cancel := context.WithCancel(context.Background()) + + defer func() { + s.status = STStop + cancel() + s.listener.Close() + }() + + go s.acceptHandler(ctx) + + for { + select { + + case <-s.stopCh: + return + } + } +} + +func (s *SocketService) acceptHandler(ctx context.Context) { + for { + c, err := s.listener.Accept() + if err != nil { + s.stopCh <- err + return + } + + go s.connectHandler(ctx, c) + } +} + +func (s *SocketService) connectHandler(ctx context.Context, c net.Conn) { + conn := NewConn(c, s.hbInterval, s.hbTimeout) + session := NewSession(conn) + s.sessions.Store(session.GetSessionID(), session) + + connctx, cancel := context.WithCancel(ctx) + + defer func() { + cancel() + conn.Close() + s.sessions.Delete(session.GetSessionID()) + }() + + go conn.readCoroutine(connctx) + go conn.writeCoroutine(connctx) + + if s.onConnect != nil { + s.onConnect(session) + } + + for { + select { + case err := <-conn.done: + + if s.onDisconnect != nil { + s.onDisconnect(session, err) + } + return + + case msg := <-conn.messageCh: + if s.onMessage != nil { + s.onMessage(session, msg) + } + } + } +} + +// GetStatus get socket service status +func (s *SocketService) GetStatus() int { + return s.status +} + +// Stop stop socket service with reason +func (s *SocketService) Stop(reason string) { + s.stopCh <- errors.New(reason) +} + +// SetHeartBeat set heart beat +func (s *SocketService) SetHeartBeat(hbInterval time.Duration, hbTimeout time.Duration) error { + if s.status == STRunning { + return errors.New("Can't set heart beat on service running") + } + + s.hbInterval = hbInterval + s.hbTimeout = hbTimeout + + return nil +} + +// GetConnsCount get connect count +func (s *SocketService) GetConnsCount() int { + var count int + s.sessions.Range(func(k, v interface{}) bool { + count++ + return true + }) + return count +} + +// Unicast Unicast with session ID +func (s *SocketService) Unicast(sid string, msg *Message) { + v, ok := s.sessions.Load(sid) + if ok { + session := v.(*Session) + err := session.GetConn().SendMessage(msg) + if err != nil { + return + } + } +} + +// Broadcast Broadcast to all connections +func (s *SocketService) Broadcast(msg *Message) { + s.sessions.Range(func(k, v interface{}) bool { + s := v.(*Session) + if err := s.GetConn().SendMessage(msg); err != nil { + // log.Println(err) + } + return true + }) +} diff --git a/net/src/test/go/net/service_test.go b/net/src/test/go/net/service_test.go new file mode 100644 index 00000000..81caa21b --- /dev/null +++ b/net/src/test/go/net/service_test.go @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package net + +import ( + "fmt" + "net" + "testing" + "time" +) + +func TestService(t *testing.T) { + host := "127.0.0.1:18787" + + ss, err := NewSocketService(host) + if err != nil { + return + } + + // ss.SetHeartBeat(5*time.Second, 30*time.Second) + + ss.RegMessageHandler(HandleMessage) + ss.RegConnectHandler(HandleConnect) + ss.RegDisconnectHandler(HandleDisconnect) + + go NewClientConnect() + + timer := time.NewTimer(time.Second * 1) + go func() { + <-timer.C + ss.Stop("stop service") + t.Log("service stoped") + }() + + t.Log("service running on " + host) + ss.Serv() +} + +func HandleMessage(s *Session, msg *Message) { + fmt.Println("receive msgID:", msg) + fmt.Println("receive data:", string(msg.GetData())) +} + +func HandleDisconnect(s *Session, err error) { + fmt.Println(s.GetConn().GetName() + " lost.") +} + +func HandleConnect(s *Session) { + fmt.Println(s.GetConn().GetName() + " connected.") +} + +func NewClientConnect() { + host := "127.0.0.1:18787" + tcpAddr, err := net.ResolveTCPAddr("tcp", host) + if err != nil { + return + } + + conn, err := net.DialTCP("tcp", nil, tcpAddr) + if err != nil { + return + } + + msg := NewMessage(1, []byte("Hello Zero!")) + data, err := Encode(msg) + if err != nil { + return + } + conn.Write(data) +} diff --git a/net/src/test/go/net/session.go b/net/src/test/go/net/session.go new file mode 100644 index 00000000..223b7f2f --- /dev/null +++ b/net/src/test/go/net/session.go @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package net + +// Session struct +type Session struct { + sID string + uID string + conn *Conn + settings map[string]interface{} +} + +// NewSession create a new session +func NewSession(conn *Conn) *Session { + id := TimeUUID() + session := &Session{ + sID: id.String(), + uID: "", + conn: conn, + settings: make(map[string]interface{}), + } + + return session +} + +// GetSessionID get session ID +func (s *Session) GetSessionID() string { + return s.sID +} + +// BindUserID bind a user ID to session +func (s *Session) BindUserID(uid string) { + s.uID = uid +} + +// GetUserID get user ID +func (s *Session) GetUserID() string { + return s.uID +} + +// GetConn get zero.Conn pointer +func (s *Session) GetConn() *Conn { + return s.conn +} + +// SetConn set a zero.Conn to session +func (s *Session) SetConn(conn *Conn) { + s.conn = conn +} + +// GetSetting get setting +func (s *Session) GetSetting(key string) interface{} { + + if v, ok := s.settings[key]; ok { + return v + } + + return nil +} + +// SetSetting set setting +func (s *Session) SetSetting(key string, value interface{}) { + s.settings[key] = value +} diff --git a/net/src/test/go/net/uuid.go b/net/src/test/go/net/uuid.go new file mode 100644 index 00000000..b1f3f6ab --- /dev/null +++ b/net/src/test/go/net/uuid.go @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package net + +import ( + "sync/atomic" + "time" +) + +type UUID [16]byte + +var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix() +var hardwareAddr []byte +var clockSeq uint32 + +func TimeUUID() UUID { + return FromTime(time.Now()) +} + +func FromTime(aTime time.Time) UUID { + var u UUID + + utcTime := aTime.In(time.UTC) + t := uint64(utcTime.Unix()-timeBase)*10000000 + uint64(utcTime.Nanosecond()/100) + u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t) + u[4], u[5] = byte(t>>40), byte(t>>32) + u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48) + + clock := atomic.AddUint32(&clockSeq, 1) + u[8] = byte(clock >> 8) + u[9] = byte(clock) + + copy(u[10:], hardwareAddr) + + u[6] |= 0x10 // set version to 1 (time based uuid) + u[8] &= 0x3F // clear variant + u[8] |= 0x80 // set to IETF variant + + return u +} + +func (u UUID) String() string { + var offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} + const hexString = "0123456789abcdef" + r := make([]byte, 36) + for i, b := range u { + r[offsets[i]] = hexString[b>>4] + r[offsets[i]+1] = hexString[b&0xF] + } + r[8] = '-' + r[13] = '-' + r[18] = '-' + r[23] = '-' + return string(r) +}