feat[go]: 支持go的单机服务器

This commit is contained in:
godotg
2022-09-14 16:29:17 +08:00
parent 4b5f1bd3eb
commit ead780dff4
13 changed files with 990 additions and 0 deletions
+17
View File
@@ -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 下载包
+134
View File
@@ -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)
}
+3
View File
@@ -0,0 +1,3 @@
module gonet
go 1.19
+30
View File
@@ -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()
}
+83
View File
@@ -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")
}
+36
View File
@@ -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()))
}
+166
View File
@@ -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
}
}
}
+29
View File
@@ -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
)
+78
View File
@@ -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)
}
+198
View File
@@ -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
})
}
+79
View File
@@ -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)
}
+73
View File
@@ -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
}
+64
View File
@@ -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)
}