接入服务器和后端业务服务其维持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接入服务器的更多相关文章

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

    TODO:Golang语言TCP/UDP协议重用地址端口 这是一个简单的包来解决重用地址的问题. go net包(据我所知)不允许设置套接字选项. 这在尝试进行TCP NAT时尤其成问题,其需要在同一 ...

  2. golang实现udp接入服务器

    前端通过udp与接入服务器连接,接入服务器与后端tcp服务器维持tcp连接.目录结构及后端tcp服务器代码同上一篇博客. main.go package main import ( "lot ...

  3. golang中tcp socket粘包问题和处理

    转自:http://www.01happy.com/golang-tcp-socket-adhere/ 在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据 ...

  4. golang 解决 TCP 粘包问题

    什么是 TCP 粘包问题以及为什么会产生 TCP 粘包,本文不加讨论.本文使用 golang 的 bufio.Scanner 来实现自定义协议解包. 协议数据包定义 本文模拟一个日志服务器,该服务器接 ...

  5. Golang 编写 Tcp 服务器

    Golang 作为广泛用于服务端和云计算领域的编程语言,tcp socket 是其中至关重要的功能.无论是 WEB 服务器还是各类中间件都离不开 tcp socket 的支持. Echo 服务器 拆包 ...

  6. golang实现tcp编程

    实现简单的tcp服务 package main import ( "fmt" "net" ) func main() { fmt.Println("服 ...

  7. golang之tcp自动重连

    操作系统: CentOS 6.9_x64 go语言版本: 1.8.3 问题描述 现有一个tcp客户端程序,需定期从服务器取数据,但由于种种原因(网络不稳定等)需要自动重连. 测试服务器示例代码: /* ...

  8. golang:TCP总结

    在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的一个进程."IP地址+端口号"就对应一个socket.欲建立连接的两个进程各自有一个 ...

  9. 6行代码解决golang TCP粘包

    转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用golang的bufio.Scanner来实现 ...

随机推荐

  1. C/C++中volatile关键字详解 (转)

    1. 为什么用volatile? C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier.这是 BS 在 "The ...

  2. github版本库使用详细图文教程(命令行及图形界面版)

    投稿:mdxy-dxy 字体:[增加 减小] 类型:转载 时间:2015-08-06我要评论 今天我们就来学习github的使用,我们将用它来管理我们的代码,你会发现它的好处的,当然是要在本系列教程全 ...

  3. R与数据分析旧笔记(十七) 主成分分析

    主成分分析 主成分分析 Pearson于1901年提出的,再由Hotelling(1933)加以发展的一种多变量统计方法 通过析取主成分显出最大的个别差异,也用来削减回归分析和聚类分析中变量的数目 可 ...

  4. JS笔记 入门第二

    输出内容 document.write(); alert("hello!"); alert(mynum); </script> 注:alert弹出消息对话框(包含一个确 ...

  5. 生成输出url时,使用CSS来控制超链接的格式

    在前文<生成输出url>中的第5点,介绍了使用ActionLink生成输出url中指定html标签属性. 例如, 假设Global.asax中的路由定义为: public static v ...

  6. SQL语句的MINUS,INTERSECT和UNION ALL

    SQL语句中的三个关键字:MINUS(减去),INTERSECT(交集)和UNION ALL(并集); 关于集合的概念,中学都应该学过,就不多说了.这三个关键字主要是对数据库的查询结果进行操作,正如其 ...

  7. android和Vitamio使用比较

    在开始接触udp组播的时候先使用的Vitamio,播放时候声音卡顿 画面也会出现卡顿,后来又使用了VLC,画面挺好,,但是声音卡顿.最后不断测试发现是由于设备底层驱动处理视频部分有问题,导致程序播出的 ...

  8. JAVA FILE or I/O学习 - 补充CopyFiles功能

    public class CopyFiles { public static void main(String[] args) { CopyFiles copyFiles = new CopyFile ...

  9. HBA简介及原理

    HBA,即主机总线适配器英文“Host Bus Adapter”缩写.是一个使计算机在服务器和存储装置间提供输入/输出(I/O)处理和物理连接的电路板和/或集成电路适配器. 简介 主机总线适配器(Ho ...

  10. Ubuntu14(64位) 集群环境下安装Hadoop2.4

    经过前边的积累,今天最终实现了集群环境下部署Hadoop.并成功执行了官方的样例. 工作例如以下: 两台机器: NameNode:上网小本,3G内存.机器名:YP-X100e,IP:192.168.1 ...