mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-14 08:18:50 +00:00
feat[go]: 支持go的单机服务器
This commit is contained in:
1 parent
4b5f1bd3eb
commit
ead780dff4
13 files changed
+990
No files matched your search
@@ -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 下载包
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module gonet
|
||||||
|
|
||||||
|
go 1.19
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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()))
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user