golang实现tcp接入服务器

接入服务器和后端业务服务其维持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)
        }
    }
}

golang实现tcp接入服务器

时间: 2024-08-05 00:11:14

golang实现tcp接入服务器的相关文章

golang中tcp socket粘包问题和处理

转自:http://www.01happy.com/golang-tcp-socket-adhere/ 在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据交互格式是一个json格式的字符串: {"Id":1,"Name":"golang","Message":"message"} 当客户端发送数据给服务端的时候,如果服务端没有及时接收,客户端又发送了一条数据上来

[UMU 学 golang](3) TCP Echo Server

测试需要,以前用 C + libevent 写了一个 TCP Echo Server,返回服务器时间.客户端地址信息和客户端发送的原内容.为了水一篇,现在改为 golang 实现. package main import ( "fmt" "io" "net" "os" "time" ) const BUFFER_SIZE = 1024 * 4 var buffer = make([]byte, BUFFER

TODO:Golang语言TCP/UDP协议重用地址端口

这是一个简单的包来解决重用地址的问题. go net包(据我所知)不允许设置套接字选项. 这在尝试进行TCP NAT时尤其成问题,其需要在同一TCP端口上进行侦听和拨号的过程. 这个包使我有可能实现这个功能. 这是一个非常小众的用例,但也许这个软件包可以随着时间的推移变得更普遍. 此包允许从同一TCP端口侦听和拨号:不能使用同一UDP端口监听,但可以监听同一UDP端口. 这意味着设置了以下sockopts: syscall.SO_REUSEADDR syscall.SO_REUSEPORT 例子

[Golang] 从零開始写Socket Server(2): 自己定义通讯协议

在上一章我们做出来一个最基础的demo后,已经能够初步实现Server和Client之间的信息交流了~ 这一章我会介绍一下怎么在Server和Client之间实现一个简单的通讯协议.从而增强整个信息交流过程的稳定性. 在Server和client的交互过程中,有时候非常难避免出现网络波动,而在通讯质量较差的时候,Client有可能无法将信息流一次性完整发送,终于传到Server上的信息非常可能变为非常多段. 例如以下图所看到的.本来应该是分条传输的json.结果由于一些原因连接在了一起,这时候就

golang中解决tcp传输中的粘包问题

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> golang中解决tcp传输中的粘包问题 - Programmer小卫 - 博客频道 - CSDN.NET Programmer小卫 故不积跬步,无以至千里.不积小流,无以成江海. 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书

Golang Tcp粘包处理(转)

在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据交互格式是一个json格式的字符串: {"Id":1,"Name":"golang","Message":"message"} 当客户端发送数据给服务端的时候,如果服务端没有及时接收,客户端又发送了一条数据上来,这时候服务端才进行接收的话就会收到两个连续的字符串,形如: {"Id":1,&qu

golang tcp socket

golang tcp socket编程和http差不多的感觉,也是请求应答的方式,TCP请求需要(ip地址,协议,端口)这三项,跟http请求差不多,我们现在来模拟一个请求和应答来,请求的一方我们就叫做client.go package main import ( "fmt" "io/ioutil" "net" "os" ) func main() { tcpAddr, err := net.ResolveTCPAddr(&q

golang tcp 2 unix socket proxy

想将mysql 的TCP 封死,所有外部链接由我的proxy来控制,so 写了一个 tcp 转 unix socket 的 proxy. package main import ( "os" "fmt" "net" "io" "sync" "time" ) type proxy struct{ Host string Port string Local string } func run

Golang实现简单tcp服务器04 -- 服务器的粘包处理

服务器的粘包处理 什么是粘包 一个完成的消息可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题 TCP粘包和拆包产生的原因 应用程序写入数据的字节大小大于套接字发送缓冲区的大小 进行MSS大小的TCP分段.MSS是最大报文段长度的缩写.MSS是TCP报文段中的数据字段的最大长度.数据字段加上TCP首部才等于整个的TCP报文段.所以MSS并不是TCP报文段的最大长度,而是:MSS=TCP报文段长度-TCP首部长度 以太网的payload