Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-socket

收集一些golang常用的一些技术。


参考【leaf】 初步学着编写一个tcp服务器,一个连接他的client。

服务器部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
"net"
"os"
)

func Start() {
l, err := net.Listen("tcp", "127.0.0.1:8866")
if err != nil {
fmt.Println(err.Error())
return
}
go main_loop(l)

// ...
}
func main_loop(l net.Listener) {
for {
conn, err := l.Accept()
if err != nil {
fmt.Println(err.Error())
continue
}
go client_loop(conn)
}
}

func client_loop(conn net.Conn) {
fmt.Println(conn.RemoteAddr().String())
// ...
}

客户端部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8866")
if err != nil {
fmt.Println(err)
return
}

go handleClient(conn)
// ...
}

func handleClient(conn net.Conn) {
//....
}

通过让主线程等待事件

1
2
3
4
5
6
7
8
9
10
import (
"os/signal"
)

func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
sig := <-c
fmt.Println(sig.String())
}

数据解析、填报代码

下面的代码是简单的从socket里面读取数据,在数据头上增加一个头,little-endian方式的uint16。后面是一块数据.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import (
"encoding/binary"
"errors"
"io"
"net"
)

// --------------
// | len | data |
// --------------
type MsgPack struct {
maxMsgLen uint16
}

func NewMsgPack() *MsgPack {
p := new(MsgPack)
p.maxMsgLen = 4096

return p
}

func (c *MsgPack) ReadPack(session net.Conn) ([]byte, error) {
var headData []byte = make([]byte, 2)
if _, err := io.ReadFull(session, headData); err != nil {
return nil, err
}
msgLen := uint16(binary.LittleEndian.Uint16(headData))
if msgLen > c.maxMsgLen {
return nil, errors.New("message too long")
}

msgData := make([]byte, msgLen)
if _, err := io.ReadFull(session, msgData); err != nil {
return nil, err
}

return msgData, nil
}

// goroutine safe
func (p *MsgPack) WritePack(conn net.Conn, args ...[]byte) error {
// get len
var msgLen uint16
for i := 0; i < len(args); i++ {
msgLen += uint16(len(args[i]))
}

// check len
if msgLen > p.maxMsgLen {
return errors.New("message too long")
}

msg := make([]byte, uint32(2)+uint32(msgLen))

// write len
binary.LittleEndian.PutUint16(msg, uint16(msgLen))

// write data
l := 2
for i := 0; i < len(args); i++ {
copy(msg[l:], args[i])
l += len(args[i])
}

conn.Write(msg)

return nil
}

发送数据的实例

1
2
buf := []byte("hello dial person! abel is online!")
pack.WritePack(conn, buf)

知识点

  • 通过struct组织数据结构
  • 启动go里面的一个进程来处理数据包
  • 挂载signal处理
  • tcp的包拆解
  • 大端小端解析integer的基本用法
  • 报错处理