golang实现tcp接入服务器

ciaos · · 10538 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

接入服务器和后端业务服务其维持tcp连接,多个前端请求通过接入服务器访问后端业务服务器,接入服务器可以方便增加路由功能,维护多个业务服务器,根据消息ID路由到具体的业务服务器。

项目目录如下

simplelotus
  src
    lotus
      main.go
    lotuslib
      tcplotus.go
    test
      tcpclient.go
      tcpserver.go
  install

install源码如下:

#!/usr/bin/env bash

if [ ! -f install ]; then
echo 'install must be run within its container folder' 1>&2
exit 1
fi

CURDIR=`pwd`
OLDGOPATH="$GOPATH"
export GOPATH="$CURDIR"

gofmt -w src

go install lotus

export GOPATH="$OLDGOPATH"

echo 'finished'

main.go

package main

import (
    "lotuslib"
)

const (
    ip   = "0.0.0.0"
    port = 1987
)

func main() {
    tcplotus.TcpLotusMain(ip, port)
}

tcplotus.go(和上游维持tcp连接)

package tcplotus

import (
    "encoding/json"
    "log"
    "net"
    "strconv"
    "time"
)

const (
    proxy_timeout = 5
    proxy_server  = "127.0.0.1:1988"
    msg_length    = 1024
)

type Request struct {
    reqId      int
    reqContent string
    rspChan    chan<- string // writeonly chan
}

//store request map
var requestMap map[int]*Request

type Clienter struct {
    client  net.Conn
    isAlive bool
    SendStr chan *Request
    RecvStr chan string
}

func (c *Clienter) Connect() bool {
    if c.isAlive {
        return true
    } else {
        var err error
        c.client, err = net.Dial("tcp", proxy_server)
        if err != nil {
            return false
        }
        c.isAlive = true
        log.Println("connect to " + proxy_server)
    }
    return true
}

//send msg to upstream server
func ProxySendLoop(c *Clienter) {

    //store reqId and reqContent
    senddata := make(map[string]string)
    for {
        if !c.isAlive {
            time.Sleep(1 * time.Second)
            c.Connect()
        }
        if c.isAlive {
            req := <-c.SendStr

            //construct request json string
            senddata["reqId"] = strconv.Itoa(req.reqId)
            senddata["reqContent"] = req.reqContent
            sendjson, err := json.Marshal(senddata)
            if err != nil {
                continue
            }

            _, err = c.client.Write([]byte(sendjson))
            if err != nil {
                c.RecvStr <- string("proxy server close...")
                c.client.Close()
                c.isAlive = false
                log.Println("disconnect from " + proxy_server)
                continue
            }
            //log.Println("Write to proxy server: " + string(sendjson))
        }
    }
}

//recv msg from upstream server
func ProxyRecvLoop(c *Clienter) {
    buf := make([]byte, msg_length)
    recvdata := make(map[string]string, 2)
    for {
        if !c.isAlive {
            time.Sleep(1 * time.Second)
            c.Connect()
        }
        if c.isAlive {
            n, err := c.client.Read(buf)
            if err != nil {
                c.client.Close()
                c.isAlive = false
                log.Println("disconnect from " + proxy_server)
                continue
            }
            //log.Println("Read from proxy server: " + string(buf[0:n]))

            if err := json.Unmarshal(buf[0:n], &recvdata); err == nil {
                reqidstr := recvdata["reqId"]
                if reqid, err := strconv.Atoi(reqidstr); err == nil {
                    req, ok := requestMap[reqid]
                    if !ok {
                        continue
                    }
                    req.rspChan <- recvdata["resContent"]
                }
                continue
            }
        }
    }
}

//one handle per request
func handle(conn *net.TCPConn, id int, tc *Clienter) {

    data := make([]byte, msg_length)
    handleProxy := make(chan string)
    request := &Request{reqId: id, rspChan: handleProxy}

    requestMap[id] = request
    for {
        n, err := conn.Read(data)
        if err != nil {
            log.Println("disconnect from " + conn.RemoteAddr().String())
            conn.Close()
            delete(requestMap, id)
            return
        }
        request.reqContent = string(data[0:n])
        //send to proxy
        select {

        case tc.SendStr <- request:
        case <-time.After(proxy_timeout * time.Second):
            //proxyChan <- &Request{cancel: true, reqId: id}
            _, err = conn.Write([]byte("proxy server send timeout."))
            if err != nil {
                conn.Close()
                delete(requestMap, id)
                return
            }
            continue
        }

        //read from proxy
        select {
        case rspContent := <-handleProxy:
            _, err := conn.Write([]byte(rspContent))
            if err != nil {
                conn.Close()
                delete(requestMap, id)
                return
            }
        case <-time.After(proxy_timeout * time.Second):
            _, err = conn.Write([]byte("proxy server recv timeout."))
            if err != nil {
                conn.Close()
                delete(requestMap, id)
                return
            }
            continue
        }
    }
}

func TcpLotusMain(ip string, port int) {
    //start tcp server
    listen, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(ip), port, ""})
    if err != nil {
        log.Fatalln("listen port error")
        return
    }
    log.Println("start tcp server " + ip + " " + strconv.Itoa(port))
    defer listen.Close()

    //start proxy connect and loop
    var tc Clienter
    tc.SendStr = make(chan *Request, 1000)
    tc.RecvStr = make(chan string)
    tc.Connect()
    go ProxySendLoop(&tc)
    go ProxyRecvLoop(&tc)

    //listen new request
    requestMap = make(map[int]*Request)
    var id int = 0
    for {

        conn, err := listen.AcceptTCP()
        if err != nil {
            log.Println("receive connection failed")
            continue
        }
        id++
        log.Println("connected from " + conn.RemoteAddr().String())
        go handle(conn, id, &tc)

    }
}

测试代码如下:

tcpserver.go

package main

import (
    "encoding/json"
    "fmt"
    "net"
)

const (
    msg_length = 1024
)

func Echo(c net.Conn) {
    data := make([]byte, msg_length)
    defer c.Close()

    var recvdata map[string]string
    recvdata = make(map[string]string, 2)
    var senddata map[string]string
    senddata = make(map[string]string, 2)

    for {
        n, err := c.Read(data)
        if err != nil {
            fmt.Printf("read message from lotus failed")
            return
        }

        if err := json.Unmarshal(data[0:n], &recvdata); err == nil {
            senddata["reqId"] = recvdata["reqId"]
            senddata["resContent"] = "Hello " + recvdata["reqContent"]

            sendjson, err := json.Marshal(senddata)
            _, err = c.Write([]byte(sendjson))
            if err != nil {
                fmt.Printf("disconnect from lotus server")
                return
            }
        }
    }
}

func main() {
    fmt.Printf("Server is ready...\n")
    l, err := net.Listen("tcp", ":1988")
    if err != nil {
        fmt.Printf("Failure to listen: %s\n", err.Error())
    }

    for {
        if c, err := l.Accept(); err == nil {
            go Echo(c) //new thread
        }
    }
}

tcpclient.go

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "time"
)

type Clienter struct {
    client  net.Conn
    isAlive bool
    SendStr chan string
    RecvStr chan string
}

func (c *Clienter) Connect() bool {
    if c.isAlive {
        return true
    } else {
        var err error
        c.client, err = net.Dial("tcp", "127.0.0.1:1987")
        if err != nil {
            fmt.Printf("Failure to connet:%s\n", err.Error())
            return false
        }
        c.isAlive = true
    }
    return true
}

func (c *Clienter) Echo() {
    line := <-c.SendStr
    c.client.Write([]byte(line))
    buf := make([]byte, 1024)
    n, err := c.client.Read(buf)
    if err != nil {
        c.RecvStr <- string("Server close...")
        c.client.Close()
        c.isAlive = false
        return
    }
    time.Sleep(1 * time.Second)
    c.RecvStr <- string(buf[0:n])
}

func Work(tc *Clienter) {
    if !tc.isAlive {
        if tc.Connect() {
            tc.Echo()
        } else {
            <-tc.SendStr
            tc.RecvStr <- string("Server close...")
        }
    } else {
        tc.Echo()
    }
}
func main() {
    var tc Clienter
    tc.SendStr = make(chan string)
    tc.RecvStr = make(chan string)
    if !tc.Connect() {
        return
    }
    r := bufio.NewReader(os.Stdin)
    for {
        switch line, ok := r.ReadString('\n'); true {
        case ok != nil:
            fmt.Printf("bye bye!\n")
            return
        default:
            go Work(&tc)
            tc.SendStr <- line
            s := <-tc.RecvStr
            fmt.Printf("back:%s\n", s)
        }
    }
}

有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:ciaos

查看原文:golang实现tcp接入服务器

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

10538 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传