mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-14 08:58:59 +00:00
feat[go]: 支持go的单机服务器
This commit is contained in:
@@ -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