package main

import (
    "net"
    "time"
)

// 初始化代理服务
func initProxy() {
    Log.Infof("Proxying %s -> %s\n", Config.Bind, Config.Backend)
    server, err := net.Listen("tcp", Config.Bind)
    if err != nil {
        Log.Fatal(err)
    }
    // 等待的队列长度
    waitQueue := make(chan net.Conn, Config.WaitQueueLen)
    // 最大并发连接
    connPools := make(chan bool, Config.MaxConn)
    for i := 0; i < Config.MaxConn; i++ {
        connPools <- true
    }
    // 等待连接处理
    go waitConn(waitQueue, connPools)
    // 接收连接并抛给管道处理
    for {
        conn, err := server.Accept()
        if err != nil {
            Log.Error(err)
            continue
        }
        Log.Infof("Received connection from %s.\n", conn.RemoteAddr())
        waitQueue <- conn
    }
}

// 连接数控制
func waitConn(waitQueue chan net.Conn, connPools chan bool) {
    for conn := range waitQueue {
        // 接收一个链接,连接池释放一个
        <-connPools
        go func(conn net.Conn) {
            handleConn(conn)
            // 链接处理完毕,增加
            connPools <- true
            Log.Infof("Closed connection from %s.\n", conn.RemoteAddr())
        }(conn)
    }
}

// 处理连接
func handleConn(conn net.Conn) {
    defer conn.Close()
    // 根据链接哈希选择机器
    proxySvr, ok := getBackendSvr(conn)
    if !ok {
        return
    }
    // 链接远程代理服务器
    remote, err := net.Dial("tcp", proxySvr.identify)
    if err != nil {
        Log.Error(err)
        proxySvr.failTimes++
        return
    }
    // 等待双向连接完成
    complete := make(chan bool, 2)
    oneSwitch := make(chan bool, 1)
    otherSwitch := make(chan bool, 1)
    // 将当前客户端链接发送的数据发送给远程被代理的服务器
    go transaction(conn, remote, complete, oneSwitch, otherSwitch)
    // 将远程服务返回的数据返回给客户端
    go transaction(remote, conn, complete, otherSwitch, oneSwitch)
    <-complete
    <-complete
    remote.Close()
}

// 数据交换传输(从from读数据,再写入to)
func transaction(from, to net.Conn, complete, oneSwitch, otherSwitch chan bool) {
    var err error
    var read int
    bytes := make([]byte, 1024)
    for {
        select {
        case <-otherSwitch:
            complete <- true
            return
        default:
            timeOutSec := time.Duration(Config.Timeout) * time.Second
            // 设置超时时间
            from.SetReadDeadline(time.Now().Add(timeOutSec))
            read, err = from.Read(bytes)
            if err != nil {
                complete <- true
                oneSwitch <- true
                return
            }
            // 设置超时时间
            to.SetWriteDeadline(time.Now().Add(timeOutSec))
            _, err = to.Write(bytes[:read])
            if err != nil {
                complete <- true
                oneSwitch <- true
                return
            }
        }
    }
}

proxy.go的更多相关文章

  1. 实现代理设置proxy

    用户在哪些情况下是需要设置网络代理呢? 1. 内网上不了外网,需要连接能上外网的内网电脑做代理,就能上外网:多个电脑共享上外网,就要用代理: 2.有些网页被封,通过国外的代理就能看到这被封的网站:3. ...

  2. Could not create SSL connection through proxy serve-svn

    RA layer request failedsvn: Unable to connect to a repository at URL xxxxxx 最后:Could not create SSL ...

  3. could not initialize proxy - no Session

    这是一个精典的问题:因为我们在hibernate里面load一个对象出来时,用到的是代理对象,也就是说当我们在执行load方法时并没有发sql语句,而是返回一个proxy对象.只有当们具体用到哪个ge ...

  4. ABP源码分析三十七:ABP.Web.Api Script Proxy API

    ABP提供Script Proxy WebApi为所有的Dynamic WebApi生成访问这些WebApi的JQuery代理,AngularJs代理以及TypeScriptor代理.这些个代理就是j ...

  5. Java设计模式之代理模式(Proxy)

    前言: 最近在研究Retrofit开源框架的时候,其主要核心代码是通过注解标示参数,动态代理模式实现具体接口,反射机制进行参数解析,最终实现发送请求.其实之前在学习Xutils源码的时候,Xutils ...

  6. 设计模式(十三)代理模式(Proxy Pattern)

    一.引言 在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代 ...

  7. 设计模式-代理模式(Proxy Model)

    文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/5988145.html 目录 1.写在前面 2.概述 3.目的 4.结构组成 5.实现 5 ...

  8. 神秘代理-Proxy

    前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求]设计:Movea ...

  9. 12,13 Proxy和Reflect

    Proxy和Reflect Proxy(代理) Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程 ...

  10. nova instance出错:"message": "Proxy error: 502 Read from server failed

    执行 $ nova resize instance1 时候出错: {, "details": " File \"/opt/stack/nova/nova/com ...

随机推荐

  1. rails将类常量重构到数据库对应的表中之一

    问题是这样:原来代码.html.erb页面中有一个select元素,其每个item对应的是model中的类常量: <%= f.select :pay_type,Order::PAYMENT_TY ...

  2. 关于MySQL 5.6.24 解压缩版重启电脑后,无法启动的问题

    最近的项目需要用到mysql,想起以前安装过,就得应该没啥问题.也不知道是软件更新换代的问题,还是版权问题,网上找的msi版本的mysql都很难安装,一开始要安装.NET,我忍了,然后又要安装Visu ...

  3. JAVA 创建对象4种方法

    java创建对象的几种方式 博客分类: java   (1) 用new语句创建对象,这是最常见的创建对象的方法.(2) 运用反射手段,调用java.lang.Class或者java.lang.refl ...

  4. Android之Drawable

    Android 中图片和常见的颜色都可以是一个Drawable. Drawable可以方便我们做出一些特殊的UI效果,这一点在UI相关的开发工作中尤其重要.其主要优点有: 使用简单,比自定义View成 ...

  5. sublime使用package control安装插件

    sublime本身可以集成Package Control来进行插件安装,非常方便. 1. 安装package control(插件包管理) 1.1 进入https://packagecontrol.i ...

  6. P3370 【模板】字符串哈希

    题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 输入输出格式 输入格式: 第一行包含一个整数N,为字符串 ...

  7. Msys+MinGW编译VLC

      说明:本文只是对官方文档进行简单的翻译总结,旨在帮助一些英文不太好的朋友.官方文档请见wiki.videolan.org/Win32CompileMSYSNew. Msys是MinGW的一个辅助工 ...

  8. 我的AngularJS学习轨迹

    开始接触Anguljar可能是在2013年初,那个时候的版本1.0.*,那个时候国内主要的中文资料AngularJS学习笔记:http://zouyesheng.com/angular.html,an ...

  9. android开发过程中遇到的坑

    在android的学习过程中,会有很多坑,我会把我遇到的,一一列下来,方便后来者查阅! 1:android-support-v4.jar and android-support-v7-appcompa ...

  10. es6(四):Symbol,Set,Map

    1.Symbol: Symbol中文意思"象征" Symbol:这是一种新的原始类型的值,表示独一无二的值(可以保证不与其它属性名冲突) Symbol()函数前面不能使用new,因 ...