一个TCP长连接设备管理后台工程(二)
2010 年 10 月 8 日
后端模型
graph BT A(终端A) --> TCPServer B(终端B) --> TCPServer C(终端C) --> TCPServer TCPServer --> Postgresql Postgresql --> HTTPServer HTTPServer --> D(ClientA) HTTPServer --> E(ClientB) HTTPServer --> F(ClientC)
后端需要设计两个服务器,一个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]