package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "runtime"
    "sync"
    "syscall"
    "time"
)

var restApiServer = flag.String("restApi", "", "listen addr for restapi") //监听rest full api 地址   ip+端口 默认值 0.0.0.0:8000  参见参数判断
var auth = flag.String("auth", "taven123", "restApi Password")  //身份认证   默认rest api  是  taven123
var gLocalConn net.Listener   //监听地址

var clientSMap map[string]net.Conn

var forwardInfo string

func main() {

    clientSMap = make(map[string]net.Conn) //key 为 ip   value 为  ip+端口创建的链接

    //解析传入的参数
    flag.Parse()

    if *restApiServer == "" {   //默认监听地址设置
        *restApiServer = "0.0.0.0:8000"  //默认值
    }

    go StartHttpServer(*restApiServer)//开启http 监听

    log.Println("restApiServer:", *restApiServer)
    fmt.Println("------------启动成功------------")

    //开启线程同步锁
    var w sync.WaitGroup
    w.Add(2)  //默认监听两个链接  

    //开一个并发线程,接收退出信号
    go func() {
        c := make(chan os.Signal, 1)  //信号量通道
        signal.Notify(c, os.Interrupt, syscall.SIGTERM)  //监听系统信号  
        n := 0
        f := func() {
            <-c
            n++
            if n > 2 {
                log.Println("force shutdown")
                os.Exit(-1)
            }
            log.Println("received signal,shutdown")
            closeAllConn() //关闭对应客户端ip 对应的链接
        }
        f()
        go func() {
            for {
                f()
            }
        }()
        //执行完成一次,Done() 等同于 Add(-1),计数不为0,则阻塞
        w.Done()
    }()

    loop := func() {
        w.Done()

    }
    loop()
    w.Wait()

    fmt.Println("------------程序执行完成------------")

}

func StartHttpServer(addr string) {  //开启服务器端的信息监听

    http.HandleFunc("/ServerSummary", ServerSummary)  //获取服务状态
    http.HandleFunc("/ForwardWork", ForwardWork)  //端口映射服务

    //
    err := http.ListenAndServe(addr, http.DefaultServeMux)

    if err != nil {
        fmt.Println("ListenAndServe error: ", err.Error())
    }

}

func ServerSummary(rw http.ResponseWriter, req *http.Request) {
    log.Println("ServerSummary")
    obj := make(map[string]interface{})
    obj["runtime_NumGoroutine"] = runtime.NumGoroutine()  //当前开启协程数量
    obj["runtime_GOOS"] = runtime.GOOS  //当前操作系统类型
    obj["runtime_GOARCH"] = runtime.GOARCH //操作系统架构
    obj["restApi_Addr"] = *restApiServer   //请求rest服务
    obj["server_Time"] = time.Now()
    obj["clients_Count"] = len(clientSMap)   //客户端数量

    var clist []string
    for cId, _ := range clientSMap {  //迭代客户端来链接map
        clist = append(clist, cId)
    }
    obj["clients_List"] = clist   //客户端列表
    obj["forwardInfo"] = forwardInfo  //转向服务器信息

    res, err := json.Marshal(obj)  //生成json数据
    if err != nil {
        log.Println("json marshal:", err)
        return
    }

    rw.Header().Add("Content-Type", "application/json;charset=utf-8")  //添加响应数据头 数据格式
    _, err = rw.Write(res)
    if err != nil {
        log.Println("write err:", err)
    }
    return
}

func ForwardWork(rw http.ResponseWriter, req *http.Request) {  
    req.ParseForm()  //解析请求参数

    obj := make(map[string]interface{}) //存储状态信息
    obj["code"] = 0
    obj["msg"] = ""

    paramAuth, hasAuth := req.Form["auth"]  //获取参数  密码
    if !hasAuth {   //情况一   不存在密码
        log.Println("request no auth")
        obj["code"] = 1
        obj["msg"] = "request no auth"
        responseResult(obj, rw)  //json数据格式 响应数据
        return

    }

    if paramAuth[0] != *auth {   //情况二  存在密码    但是密码不匹配
        log.Println("request auth failed")
        obj["code"] = 1
        obj["msg"] = "request auth failed"
        responseResult(obj, rw)//json数据格式 响应数据

        return
    }

    paramStatus, hasStatus := req.Form["status"]  //status:如果是开启转发,则为1,如果是关闭转发,则为0
    if !hasStatus {  //没有开启转发  直接返回
        return

    }

    log.Println("param_status:", paramStatus)

    if paramStatus[0] == "1" {  //开启转发
        //启动服务
        paramFromAddr, hasFromAddr := req.Form["fromAddr"]  //要用来在A机器上监听的一个端口,用来给客户端连接     =====服务器
        paramToAddr, hasToAddr := req.Form["toAddr"] //把fromAddr端口的数据转发到哪个IP的端口上   ===转发到的服务器
        if gLocalConn != nil { //目标服务器是否存在链接   不存在 直接拒接
            gLocalConn.Close()  //释放资源
        }

        if hasFromAddr && hasToAddr {  //目标服务器 和 转发到服务器存在   
            go forwardPort(paramFromAddr[0], paramToAddr[0])
        }
    }

    if paramStatus[0] == "0" {   //转发状态为0 时   直接释放资源 
        //关闭服务
        closeAllConn()
        forwardInfo = ""
    }

    responseResult(obj, rw)// json格式数据响应客户端

    return

}

func responseResult(data map[string]interface{}, rw http.ResponseWriter) {// json格式数据响应客户端
    res, err := json.Marshal(data)  //生成json数据格式
    if err != nil {
        log.Println("json marshal:", err)
        return
    }

    rw.Header().Add("Content-Type", "application/json;charset=utf-8") //响应头数据格式设置
    _, err = rw.Write(res) //响应写入数据
    if err != nil {
        log.Println("write err:", err)
    }
}

func closeAllConn() {// 关闭连接
    for cId, conn := range clientSMap {
        log.Println("clientMap id:", cId)
        conn.Close() //关闭连接 conn
        delete(clientSMap, cId)  //从客户端map中删除对应的数据
    }

    if gLocalConn != nil {  //关闭元数据链接【服务端】
        gLocalConn.Close()
        log.Println("Listener Close")
    } else {
        gLocalConn = nil
        log.Println("Listener set to nil", gLocalConn)
    }
}

func forwardPort(sourcePort string, targetPort string) { //转发服务

    fmt.Println("sourcePort:", sourcePort, "targetPort:", targetPort)

    localConn, err := net.Listen("tcp", sourcePort) //目标服务器   监听服务器

    if err != nil {
        fmt.Println(err.Error())
        return
    }

    gLocalConn = localConn

    fmt.Println("服务启动成功,服务地址:", sourcePort)

    forwardInfo = fmt.Sprintf("%s - %s", sourcePort, targetPort)

    for {
        fmt.Println("Ready to Accept ...")
        sourceConn, err := gLocalConn.Accept()  //获取监听服务链接

        if err != nil {
            log.Println("server err:", err.Error())
            break
        }
        //log.Println("client", sc.id, "create session", sessionId)

        id := sourceConn.RemoteAddr().String()   //获取客户端ip地址
        clientSMap[id] = sourceConn

        fmt.Println("conn.RemoteAddr().String() :", id)

        //targetPort := "172.16.128.83:22"
        targetConn, err := net.DialTimeout("tcp", targetPort, 30*time.Second)//获取转发服务链接
               //开启协程 支持从客户端 到服务端  以及 从 服务端到客户端数据 读写
        go func() {
            _, err = io.Copy(targetConn, sourceConn)  //从sourceConn读取数据 写入到targetConn中
            if err != nil {
                //log.Fatalf("io.Copy 1 failed: %v", err)
                fmt.Println("io.Copy 1 failed:", err.Error())
            }
        }()

        go func() {
            _, err = io.Copy(sourceConn, targetConn)//从targetConn读取数据 写入到sourceConn中
            if err != nil {
                //log.Fatalf("io.Copy 2 failed: %v", err)
                fmt.Println("io.Copy 2 failed:", err.Error())
            }
        }()

    }

    //
    log.Println("forwardPort end.")

}

forwardport--源码笔记--注释的更多相关文章

  1. AsyncTask源码笔记

    AsyncTask源码笔记 AsyncTask在注释中建议只用来做短时间的异步操作,也就是只有几秒的操作:如果是长时间的操作,建议还是使用java.util.concurrent包中的工具类,例如Ex ...

  2. Java Arrays 源码 笔记

    Arrays.java是Java中用来操作数组的类.使用这个工具类可以减少平常很多的工作量.了解其实现,可以避免一些错误的用法. 它提供的操作包括: 排序 sort 查找 binarySearch() ...

  3. Tomcat8源码笔记(六)连接器Connector分析

    根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...

  4. Golang构建HTTP服务(一)--- net/http库源码笔记

    搭建一个简单的Go Web服务器 Go语言标准库 - net/http 在学习Go语言有一个很好的起点,Go语言官方文档很详细,今天我们学习的Go Web服务器的搭建就需要用到Go语言官方提供的标准库 ...

  5. Zepto源码笔记(一)

    最近在研究Zepto的源码,这是第一篇分析,欢迎大家继续关注,第一次写源码笔记,希望大家多指点指点,第一篇文章由于首次分析原因不会有太多干货,希望后面的文章能成为各位大大心目中的干货. Zepto是一 ...

  6. robotlegs2.0框架实例源码带注释

    robotlegs2.0框架实例源码带注释 Robotlegs2的Starling扩展 有个老外写了robotleges2的starling扩展,地址是 https://github.com/brea ...

  7. redis源码笔记(一) —— 从redis的启动到command的分发

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...

  8. Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目

    以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...

  9. Tomcat8源码笔记(七)组件启动Server Service Engine Host启动

    一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...

  10. Tomcat8源码笔记(五)组件Container分析

    Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...

随机推荐

  1. Https背景与证书在spring boot项目中的使用

    https背景(本人学习参考中觉得不错的几篇文章) https如何解决安全问题 HTTPS 理论基础及其在 Android 中的最佳实践 什么是https 关于https的个人总结 总所周知http是 ...

  2. angularjs作用域之transclude

    transclude是一个可选的参数.如果设置了,其值必须为true,它的默认值是false.嵌入有时被认为是一个高级主题,但某些情况下它与我们刚刚学习过的作用域之间会有非常好的配合.使用嵌入也会很好 ...

  3. 如何通过jQuery获取一个没有定高度的元素---------的自适应高度(offsetHeight的正确使用方法)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. Mongodb3.6 快速入门(一)

    一.安装 官网下载地址 文章中用的3.6community server 64 位. 安装完成后的目录如下: 二.启动Mongodb 有多种方式可以启动Mongodb,但在实际项目中,主要还是通过配置 ...

  5. SQL Server 远程更新目标表数据

    分享一个远程更新目标库数据的存储过程,适用于更新列名一致,主键为Int类型,可远程链接的数据库. ** 温馨提示:如需转载本文,请注明内容出处.** 本文连接:http://www.cnblogs.c ...

  6. python---数据类型---列表

    #列表: name = ["lc","pxm","pt"] print('------------',name[2],"----- ...

  7. JAVAEE——BOS物流项目13:Quartz概述、创建定时任务、使用JavaMail发送邮件、HighCharts概述、实现区域分区分布图

    1 学习计划 1.Quartz概述 n Quartz介绍和下载 n 入门案例 n Quartz执行流程 n cron表达式 2.在BOS项目中使用Quartz创建定时任务 3.在BOS项目中使用Jav ...

  8. 搭建centos7的开发环境3-Spark安装配置

    说起大数据开发,必然就会提到Spark,在这片博文中,我们就介绍一下Spark的安装和配置. 这是Centos7开发环境系列的第三篇,本篇的安装会基于之前的配置进行,有需要的请回复搭建centos7的 ...

  9. python笔记:#003#PyCharm 的初始设置

    PyCharm 的初始设置(知道) 目标 恢复 PyCharm 的初始设置 第一次启动 PyCharm 新建一个 Python 项目 设置 PyCharm 的字体显示 PyCharm 的升级以及其他 ...

  10. STL-Deque 源码剖析

    G++ ,cygnus\cygwin-b20\include\g++\stl_deque.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Comp ...