后端模型
后端需要设计两个服务器,一个TCP,一个HTTP。TCP主要处理与终端的长连接交互,一个TCP连接对应一台终端设备,终端设备唯一标识使用IMEI。HTTP处理与前端的交互,前端需要获取所有可用的终端设备列表,向指定的终端发送命令。所以,为了方便从ip找到对应终端,然后从对应终端找到对应的conn,我们就需要维护一个map:
type Terminal struct {
authkey string
imei string
iccid string
vin string
tboxver string
loginTime time.Time
seqNum uint16
phoneNum string
Conn net.Conn
}
var connManger map[string]*Terminal
至于为什么要定义成指针的形式,是因为定义成指针后我们可以直接修改map中元素结构体中对应的变量,而不需要重新定义一个元素再赋值。
var connManager map[string]*Terminal
connManager = make(map[string]*Terminal)
connManager["127.0.0.1:11000"]=&Terminal{}
connManager["127.0.0.1:11001"]=&Terminal{}
...
//此处能够轻松的修改对应的phoneNum修改
connManager["127.0.0.1:11001"].phoneNum = "13000000000"
相反,下面的这段代码修改起来就要繁琐不少:
var connManager map[string]Terminal
connManager = make(map[string]Terminal)
connManager["127.0.0.1:11000"]=Terminal{}
connManager["127.0.0.1:11001"]=Terminal{}
...
//此处会报错
connManager["127.0.0.1:11001"].phoneNum = "13000000000"
//此处修改需要定义一个临时变量,类似于读改写的模式
term,ok:=connManager["127.0.0.1:11001"]
term.phoneNum = "13000000000"
connManager["127.0.0.1:11001"]=term
上面的代码一处会报错
cannot assign to struct field connManager["127.0.0.1:11001"].phoneNum in map
从上面的对比就可以看到,确实是定义成指针更加方便了。
TCP的长连接模型
TCP的长连接我们选择这样的一种方式:
- 每个连接分配一个读Goroutine
- 写数据按需分配
如果熟悉socket的话,就知道socket一个服务器创建的基本步骤:
- 创建socket
- listen
- accept
其中accept一般需要轮循调用。golang也基本是同样的流程。
一个简单的TCP服务器示例:
package main
import (
"fmt"
"net"
)
type Terminal struct {
authkey string
imei string
iccid string
vin string
tboxver string
phoneNum string
Conn net.Conn
}
var connManager map[string]*Terminal
func recvConnMsg(conn net.Conn) {
addr := conn.RemoteAddr()
var term *Terminal = &Terminal{
Conn: conn,
}
term.Conn = conn
connManager[addr.String()] = term
defer func() {
delete(connManager, addr.String())
conn.Close()
}()
for {
tempbuf := make([]byte, 1024)
n, err := conn.Read(tempbuf)
if err != nil {
return
}
fmt.Println("rcv:", tempbuf[:n])
}
}
func TCPServer(addr string) {
connManager = make(map[string]*Terminal)
listenSock, err := net.Listen("tcp", addr)
if err != nil {
return
}
defer listenSock.Close()
for {
newConn, err := listenSock.Accept()
if err != nil {
continue
}
go recvConnMsg(newConn)
}
}
func main() {
TCPServer(":19903")
}
以下是用来测试的客户端代码:
package main
import (
"fmt"
"net"
"time"
)
func main() {
conn, err := net.Dial("tcp", ":19903")
if err != nil {
return
}
defer conn.Close()
var n int = 0
n, err = conn.Write([]byte("123456"))
if err != nil {
return
}
fmt.Println("len:", n)
for {
time.Sleep(time.Second * 3)
}
}
测试结果:
$ ./server
rcv: [49 50 51 52 53 54]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。