一个TCP长链接设备管理后台工程(二)

后端模型

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中元素结构体中对应的变量,而不须要从新定义一个元素再赋值。golang

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"

相反,下面的这段代码修改起来就要繁琐很多:sql

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

从上面的对比就能够看到,确实是定义成指针更加方便了。bash

TCP的长链接模型

TCP的长链接咱们选择这样的一种方式:服务器

  • 每一个链接分配一个读Goroutine
  • 写数据按需分配

若是熟悉socket的话,就知道socket一个服务器建立的基本步骤:socket

  1. 建立socket
  2. listen
  3. accept

其中accept通常须要轮循调用。golang也基本是一样的流程。tcp

一个简单的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")
}

如下是用来测试的客户端代码:ui

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]
相关文章
相关标签/搜索