一個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]
相關文章
相關標籤/搜索