Browse Source

https 、客户端与服务端连接优化

刘河 6 năm trước cách đây
mục cha
commit
eccc221e67

+ 138 - 241
README.md

@@ -6,7 +6,7 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
 目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。
 
 
-支持客户端与服务端连接中断自动重连,多路传输,大大的提高请求处理速度,go语言编写,无第三方依赖,各个平台都已经编译在release中,普通个人场景下,内存使用量在10M以下
+go语言编写,无第三方依赖,各个平台都已经编译在release中。
 
 ## 背景
 ![image](https://github.com/cnlh/easyProxy/blob/master/image/web.png?raw=true)
@@ -30,6 +30,12 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
 * [web管理](#web管理模式)(多隧道时推荐)
     * [启动](#启动)
     * [配置文件说明](#服务端配置文件)
+    * [详细使用说明](#详细说明)
+       * [http|https域名解析](#域名解析)
+       * [tcp隧道](#tcp隧道)
+       * [udp隧道](#udp隧道)
+       * [sock5代理](#sock5代理)
+       * [http正向代理](#http正向代理)
 * 单隧道模式及介绍
     * [tcp隧道模式](#tcp隧道模式)
     * [udp隧道模式](#udp隧道模式)
@@ -40,7 +46,6 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
    * [数据压缩支持](#数据压缩支持)
    * [站点密码保护](#站点保护)
    * [加密传输](#加密传输)
-   * [TCP多路复用](#多路复用)
    * [host修改](#host修改)
    * [自定义header](#自定义header)
    * [自定义404页面](#404页面配置)
@@ -48,14 +53,18 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
    * [带宽限制](#带宽限制)
 * [相关说明](#相关说明)
    * [流量统计](#流量统计)
-   * [连接池](#连接池)
    * [热更新支持](#热更新支持)
    * [获取用户真实ip](#获取用户真实ip)
    * [客户端地址显示](#客户端地址显示)
+* [简单的性能测试](#简单的性能测试)
+   * [qps](#qps)
+   * [速度测试](#速度测试)
+   * [内存和cpu](#内存和cpu)
+   * [额外消耗连接数](#额外消耗连接数)
 * [webAPI](#webAPI)
    * [客户端](#客户端)
-    * [域名代理](#域名代理)
-    * [其他代理](#其他代理)
+   * [域名代理](#域名代理)
+   * [其他代理](#其他代理)
 
 
 ## 安装
@@ -78,7 +87,31 @@ easyProxy是一款轻量级、高性能、功能最为强大的**内网穿透**
 ![image](https://github.com/cnlh/easyProxy/blob/master/image/web2.png?raw=true)
 ### 介绍
 
-可在网页上配置和管理各个tcp、udp隧道、内网站点代理等等,功能极为强大,操作也非常方便。
+可在网页上配置和管理各个tcp、udp隧道、内网站点代理,http、https解析等,功能极为强大,操作也非常方便。
+
+
+**提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件**
+
+### 启动
+
+
+- 服务端
+
+```
+ ./proxy_server
+```
+
+- 客户端
+```
+ ./proxy_server -server=ip:port -vkey=web界面中显示的密钥
+```
+
+- 配置
+
+进入web界面,公网ip:web界面端口(默认8080),密码默认为123
+
+进入web管理界面,有详细的命令
+
 ### 服务端配置文件
 - /conf/app.conf
 
@@ -88,28 +121,102 @@ httpport | web管理端口
 password | web界面管理密码
 hostPort | 域名代理模式监听端口
 tcpport  | 服务端客户端通信端口
+pemPath | ssl certFile绝对路径
+keyPath | ssl keyFile绝对路径
+httpsProxyPort | https代理监听端口
+httpProxyPort | http代理监听端口
 
-**提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件**
+### 详细说明
 
-### 启动
+#### 域名解析
 
+**适用范围:** 小程序开发、微信公众号开发、产品演示
 
-- 服务端
+**假设场景:**
+- 有一个域名proxy.com,有一台公网机器ip为1.1.1.1
+- 两个内网开发站点127.0.0.1:81,127.0.0.1:82
+- 想通过(http|https://)a.proxy.com访问127.0.0.1:81,通过(http|https://)b.proxy.com访问127.0.0.1:82
+
+**使用步骤**
+- 将*.proxy.com解析到公网服务器1.1.1.1
+- 在客户端管理中创建一个客户端,记录下验证密钥
+- 点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82
+-内网客户端运行
 
 ```
- ./proxy_server
+./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
 ```
+现在访问(http|https://)a.proxy.com,b.proxy.com即可成功
 
-- 客户端
+**https:** 如需使用https请在配置文件中将https端口设置为443,和将对应的证书文件路径添加到配置文件中
+
+#### tcp隧道
+
+
+**适用范围:**  ssh、远程桌面等tcp连接场景
+
+**假设场景:**
+ 想通过访问公网服务器1.1.1.1的8001端口,连接内网机器10.1.50.101的22端口,实现ssh连接
+
+**使用步骤**
+- 在客户端管理中创建一个客户端,记录下验证密钥
+- -内网客户端运行
 ```
- ./proxy_server -server=ip:port -vkey=web界面中显示的
+./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
 ```
+- 在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。
+- 访问公网服务器ip(127.0.0.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@127.0.0.1
 
-进入web管理界面,有详细的命令
+#### udp隧道
 
-- 配置
 
-进入web界面,公网ip:web界面端口(默认8080),密码默认为123
+
+**适用范围:**  内网dns解析等udp连接场景
+
+**假设场景:**
+内网有一台dns(10.1.50.102:53),在非内网环境下想使用该dns,公网服务器为1.1.1.1
+
+**使用步骤**
+- 在客户端管理中创建一个客户端,记录下验证密钥
+- -内网客户端运行
+```
+./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
+```
+- 在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。
+- 修改本机dns为127.0.0.1,则相当于使用10.1.50.202作为dns服务器
+
+#### sock5代理
+
+
+**适用范围:**  在外网环境下如同使用vpn一样访问内网设备或者资源
+
+**假设场景:**
+想将公网服务器1.1.1.1的8003端口作为socks5代理,达到访问内网任意设备或者资源的效果
+
+**使用步骤**
+- 在客户端管理中创建一个客户端,记录下验证密钥
+- -内网客户端运行
+```
+./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
+```
+- 在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。
+- 在外网环境的本机配置socks5代理,ip为公网服务器ip(127.0.0.1),端口为填写的监听端口(8003),即可畅享内网了
+
+#### http正向代理
+
+**适用范围:**  在外网环境下使用http代理访问内网站点
+
+**假设场景:**
+想将公网服务器1.1.1.1的8004端口作为http代理,访问内网网站
+
+**使用步骤**
+- 在客户端管理中创建一个客户端,记录下验证密钥
+- -内网客户端运行
+```
+./proxy_client server=1.1.1.1:8284 -vkey=客户端的密钥
+```
+- 在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。
+- 在外网环境的本机配置http代理,ip为公网服务器ip(127.0.0.1),端口为填写的监听端口(8004),即可访问了
 
 
 
@@ -316,16 +423,6 @@ authip | 免验证ip,适用于web api
 -crypt=true
 ```
 
-### 多路复用
-
-客户端和服务器端之间的连接支持多路复用,不再需要为每一个用户请求创建一个连接,使连接建立的延迟降低,并且避免了大量文件描述符的占用。
-
-
-- 在server端加上参数 -mux=true(或在web管理中设置)
-
-```
--mux=true
-```
 
 
 ### 站点保护
@@ -373,227 +470,27 @@ authip | 免验证ip,适用于web api
 ### 流量统计
 可统计显示每个代理使用的流量,由于压缩和加密等原因,会和实际环境中的略有差异
 
-### 连接池
- easyProxy会预先和后端服务建立起指定数量的连接,每次接收到用户请求后,会从连接池中取出一个连接和用户连接关联起来,避免了等待与后端服务建立连接时间。
+## 简单性能测试
 
-## webAPI
+### qps
+![image](https://github.com/cnlh/easyProxy/blob/master/image/qps.png?raw=true)
+### 速度测试
+**测试环境:** 1M带宽云服务器,理论125kb/s,带宽与代理无关,与服务器关系较大。
+![image](https://github.com/cnlh/easyProxy/blob/master/image/speed.png?raw=true)
 
-### 客户端
 
-#### 添加客户端
-```
-POST /client/add/
-```
-参数 | 含义
----|---
-remark | 备注
-u | 用户名
-p | 密码
-compress | 压缩(snappy或空)
-crypt | 是否加密(1或者0)
-mux | 是否TCP复用(1或者0)
-rate_limit|带宽限制
-flow_limit|流量限制
+### 内存和cpu
 
-#### 添加客户端
-```
-POST /client/edit/
-```
-参数 | 含义
----|---
-id | id
-remark | 备注
-u | 用户名
-p | 密码
-compress | 压缩(snappy或空)
-crypt | 是否加密(1或者0)
-mux | 是否TCP复用(1或者0)
-rate_limit|带宽限制
-flow_limit|流量限制
+**1000次性能测试后**
+![image](https://github.com/cnlh/easyProxy/blob/master/image/cpu1.png?raw=true)
 
-#### 更改状态
-```
-POST /client/changestatus/
-```
-参数 | 含义
----|---
-id | id
-status|1或0
+**启动时**
+![image](https://github.com/cnlh/easyProxy/blob/master/image/cpu2.png?raw=true)
 
-#### 删除客户端
-```
-POST /client/del/
-```
-参数 | 含义
----|---
-id | id
-#### 获取单个客户端
-```
-POST /client/getclient/
-```
-参数 | 含义
----|---
-id | id
-#### 获取客户端列表
-```
-POST /client/list/
-```
+### 额外消耗连接数
+为了最大化的提升效率和并发,客户端与服务端之间仅两条tcp连接,减少建立连接的时间消耗和多余tcp连接对机器性能的影响。
 
-参数 | 含义
----|---
-start | 开始
-length | 长度
-
-### 域名代理
-
-#### 添加域名代理
-
-```
-POST /index/addhost/
-```
-
-参数 | 含义
----|---
-host | 域名
-target | 内网目标地址
-header | header修改
-hostchange | host修改
-remark | 备注
-client_id | 客户端id
-#### 删除域名代理
-```
-POST /index/delhost/
-```
-
-参数 | 含义
----|---
-host | 域名
-
-#### 修改域名代理
-```
-POST /index/edithost/
-```
-
-参数 | 含义
----|---
-nhost | 修改后的域名
-host | 修改之前的域名
-target | 内网目标地址
-header | header修改
-hostchange | host修改
-remark | 备注
-client_id | 客户端id
-
-#### 获取域名代理列表
-```
-POST /index/hostlist/
-```
-
-参数 | 含义
----|---
-start | 开始
-length | 长度
-client_id | 客户端id(为空获取所有)
-#### 获取单个host
-```
-POST /index/gethost/
-```
-
-参数 | 含义
----|---
-host|域名
-
-
-### 其他代理
-
-#### 获取隧道列表
-
-```
-POST /index/gettunnel/
-```
-
-参数 | 含义
----|---
-client_id|客户端id(为空则忽略客户端限制)
-type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer,为空则忽略类型限制)
-start|开始
-length|长度
-
-#### 添加
-
-```
-POST /index/add/
-```
-
-参数 | 含义
----|---
-port|监听端口
-type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer)
-start|开始
-u|验证用户名
-p|验证密码
-compress|压缩(空或snappy)
-crypt|是否加密(1或0)
-mux|是否tcp复用(1或0)
-use_client|是否使用客户端配置(1或0)
-remark|备注
-client_id|客户端id
-
-#### 修改
-
-```
-POST /index/edit/
-```
-
-参数 | 含义
----|---
-id|id
-port|监听端口
-type|类型(udpServer、tunnelServer、socks5Server、httpProxyServer)
-start|开始
-u|验证用户名
-p|验证密码
-compress|压缩(空或snappy)
-crypt|是否加密(1或0)
-mux|是否tcp复用(1或0)
-use_client|是否使用客户端配置(1或0)
-remark|备注
-client_id|客户端id
-
-#### 停止隧道
-
-```
-POST /index/stop/
-```
-
-参数 | 含义
----|---
-id|id
-
-#### 删除隧道
-
-```
-POST /index/del/
-```
-
-参数 | 含义
----|---
-id|id
-#### 开始隧道
-
-```
-POST /index/start/
-```
-
-参数 | 含义
----|---
-id|id
-#### 获取单条隧道详细
-
-```
-POST /index/getonetunnel/
-```
+## webAPI
 
-参数 | 含义
----|---
-id|id
+为方便第三方扩展,在web模式下可利用webAPI进行相关操作,详情见
+[webAPI文档](https://github.com/cnlh/easyProxy/wiki/webAPI%E6%96%87%E6%A1%A3)

+ 168 - 108
bridge/bridge.go

@@ -5,47 +5,33 @@ import (
 	"github.com/cnlh/easyProxy/utils"
 	"log"
 	"net"
-	"strconv"
 	"sync"
 	"time"
 )
 
-type list struct {
-	connList chan *utils.Conn
-}
-
-func (l *list) Add(c *utils.Conn) {
-	l.connList <- c
-}
-
-func (l *list) Pop() *utils.Conn {
-	return <-l.connList
-}
-func (l *list) Len() int {
-	return len(l.connList)
-}
-
-func newList() *list {
-	l := new(list)
-	l.connList = make(chan *utils.Conn, 1000)
-	return l
+type Client struct {
+	tunnel        *utils.Conn
+	signal        *utils.Conn
+	linkMap       map[int]*utils.Link
+	linkStatusMap map[int]bool
+	stop          chan bool
+	sync.RWMutex
 }
 
 type Bridge struct {
-	TunnelPort int                 //通信隧道端口
-	listener   *net.TCPListener    //server端监听
-	SignalList map[int]*list       //通信
-	TunnelList map[int]*list       //隧道
+	TunnelPort int              //通信隧道端口
+	listener   *net.TCPListener //server端监听
+	Client     map[int]*Client
 	RunList    map[int]interface{} //运行中的任务
 	lock       sync.Mutex
 	tunnelLock sync.Mutex
+	clientLock sync.Mutex
 }
 
 func NewTunnel(tunnelPort int, runList map[int]interface{}) *Bridge {
 	t := new(Bridge)
 	t.TunnelPort = tunnelPort
-	t.SignalList = make(map[int]*list)
-	t.TunnelList = make(map[int]*list)
+	t.Client = make(map[int]*Client)
 	t.RunList = runList
 	return t
 }
@@ -103,11 +89,12 @@ func (s *Bridge) cliProcess(c *utils.Conn) {
 }
 
 func (s *Bridge) closeClient(id int) {
-	if len(s.SignalList) > 0 {
-		s.SignalList[id].Pop().WriteClose()
+	s.clientLock.Lock()
+	defer s.clientLock.Unlock()
+	if v, ok := s.Client[id]; ok {
+		v.signal.WriteClose()
+		delete(s.Client, id)
 	}
-	s.DelClientSignal(id)
-	s.DelClientTunnel(id)
 }
 
 //tcp连接类型区分
@@ -115,120 +102,193 @@ func (s *Bridge) typeDeal(typeVal string, c *utils.Conn, id int) {
 	switch typeVal {
 	case utils.WORK_MAIN:
 		//客户端已经存在,下线
-		if _, ok := s.SignalList[id]; ok {
+		s.clientLock.Lock()
+		if _, ok := s.Client[id]; ok {
+			s.clientLock.Unlock()
 			s.closeClient(id)
+		} else {
+			s.clientLock.Unlock()
+		}
+		s.clientLock.Lock()
+
+		s.Client[id] = &Client{
+			linkMap:       make(map[int]*utils.Link),
+			stop:          make(chan bool),
+			linkStatusMap: make(map[int]bool),
 		}
-		log.Println("客户端连接成功", c.Conn.RemoteAddr())
-		s.addList(s.SignalList, c, id)
+		log.Printf("客户端%d连接成功,地址为:%s", id, c.Conn.RemoteAddr())
+		s.Client[id].signal = c
+		s.clientLock.Unlock()
+		go s.GetStatus(id)
 	case utils.WORK_CHAN:
-		s.addList(s.TunnelList, c, id)
+		s.clientLock.Lock()
+		if v, ok := s.Client[id]; ok {
+			s.clientLock.Unlock()
+			v.tunnel = c
+		} else {
+			s.clientLock.Unlock()
+			return
+		}
+		go s.clientCopy(id)
 	}
 	c.SetAlive()
 	return
 }
 
-//加到对应的list中
-func (s *Bridge) addList(m map[int]*list, c *utils.Conn, id int) {
-	s.lock.Lock()
-	if v, ok := m[id]; ok {
-		v.Add(c)
-	} else {
-		l := newList()
-		l.Add(c)
-		m[id] = l
+//等待
+func (s *Bridge) waitStatus(clientId, id int) (bool) {
+	ticker := time.NewTicker(time.Millisecond * 100)
+	stop := time.After(time.Second * 10)
+	for {
+		select {
+		case <-ticker.C:
+			s.clientLock.Lock()
+			if v, ok := s.Client[clientId]; ok {
+				s.clientLock.Unlock()
+				v.Lock()
+				if vv, ok := v.linkStatusMap[id]; ok {
+					ticker.Stop()
+					v.Unlock()
+					return vv
+				}
+				v.Unlock()
+			} else {
+				s.clientLock.Unlock()
+			}
+		case <-stop:
+			return false
+		}
 	}
-	s.lock.Unlock()
+	return false
 }
 
-//得到一个tcp隧道
-func (s *Bridge) GetTunnel(id int, en, de int, crypt, mux bool) (c *utils.Conn, err error) {
-retry:
-	if c, err = s.waitAndPop(s.TunnelList, id); err != nil {
-		return
-	}
-	if _, err = c.WriteTest(); err != nil {
-		c.Close()
-		goto retry
+func (s *Bridge) SendLinkInfo(clientId int, link *utils.Link) (tunnel *utils.Conn, err error) {
+	s.clientLock.Lock()
+	if v, ok := s.Client[clientId]; ok {
+		s.clientLock.Unlock()
+		v.signal.SendLinkInfo(link)
+		if err != nil {
+			log.Println("send error:", err, link.Id)
+			s.DelClient(clientId)
+			return
+		}
+		if v.tunnel == nil {
+			err = errors.New("tunnel获取错误")
+			return
+		} else {
+			tunnel = v.tunnel
+		}
+		v.Lock()
+		v.linkMap[link.Id] = link
+		v.Unlock()
+		if !s.waitStatus(clientId, link.Id) {
+			err = errors.New("连接失败")
+			return
+		}
+	} else {
+		s.clientLock.Unlock()
+		err = errors.New("客户端未连接")
 	}
-	c.WriteConnInfo(en, de, crypt, mux)
 	return
 }
 
-//得到一个通信通道
-func (s *Bridge) GetSignal(id int) (err error, conn *utils.Conn) {
-	if v, ok := s.SignalList[id]; !ok || v.Len() == 0 {
+//得到一个tcp隧道
+func (s *Bridge) GetTunnel(id int, en, de int, crypt, mux bool) (conn *utils.Conn, err error) {
+	s.clientLock.Lock()
+	defer s.clientLock.Unlock()
+	if v, ok := s.Client[id]; !ok {
 		err = errors.New("客户端未连接")
-		return
+	} else {
+		conn = v.tunnel
 	}
-	conn = s.SignalList[id].Pop()
 	return
 }
 
-//重回slice 复用
-func (s *Bridge) ReturnSignal(conn *utils.Conn, id int) {
-	if v, ok := s.SignalList[id]; ok {
-		v.Add(conn)
-	}
-}
-
-//重回slice 复用
-func (s *Bridge) ReturnTunnel(conn *utils.Conn, id int) {
-	if v, ok := s.TunnelList[id]; ok {
-		utils.FlushConn(conn.Conn)
-		v.Add(conn)
+//得到一个通信通道
+func (s *Bridge) GetSignal(id int) (conn *utils.Conn, err error) {
+	s.clientLock.Lock()
+	defer s.clientLock.Unlock()
+	if v, ok := s.Client[id]; !ok {
+		err = errors.New("客户端未连接")
+	} else {
+		conn = v.signal
 	}
+	return
 }
 
 //删除通信通道
-func (s *Bridge) DelClientSignal(id int) {
-	s.delClient(id, s.SignalList)
+func (s *Bridge) DelClient(id int) {
+	s.closeClient(id)
 }
 
-//删除隧道
-func (s *Bridge) DelClientTunnel(id int) {
-	s.delClient(id, s.TunnelList)
-}
-
-func (s *Bridge) delClient(id int, l map[int]*list) {
-	if t := l[id]; t != nil {
-		for {
-			if t.Len() <= 0 {
-				break
-			}
-			t.Pop().Close()
+func (s *Bridge) verify(id int) bool {
+	for k := range s.RunList {
+		if k == id {
+			return true
 		}
-		delete(l, id)
 	}
+	return false
 }
+func (s *Bridge) GetStatus(clientId int) {
+	s.clientLock.Lock()
+	client := s.Client[clientId]
+	s.clientLock.Unlock()
 
-//等待
-func (s *Bridge) waitAndPop(m map[int]*list, id int) (c *utils.Conn, err error) {
-	ticker := time.NewTicker(time.Millisecond * 100)
-	stop := time.After(time.Second * 3)
+	if client == nil {
+		return
+	}
 	for {
-		select {
-		case <-ticker.C:
-			s.lock.Lock()
-			if v, ok := m[id]; ok && v.Len() > 0 {
-				c = v.Pop()
-				ticker.Stop()
-				s.lock.Unlock()
-				return
-			}
-			s.lock.Unlock()
-		case <-stop:
-			err = errors.New("client id: " + strconv.Itoa(id) + ",err: get client conn timeout")
+		if id, status, err := client.signal.GetConnStatus(); err != nil {
+			s.closeClient(clientId)
 			return
+		} else {
+			client.Lock()
+			client.linkStatusMap[id] = status
+			client.Unlock()
 		}
 	}
-	return
 }
 
-func (s *Bridge) verify(id int) bool {
-	for k := range s.RunList {
-		if k == id {
-			return true
+func (s *Bridge) clientCopy(clientId int) {
+
+	s.clientLock.Lock()
+	client := s.Client[clientId]
+	s.clientLock.Unlock()
+
+	for {
+		if id, err := client.tunnel.GetLen(); err != nil {
+			s.closeClient(clientId)
+			log.Println("读取msg id 错误", err, id)
+			break
+		} else {
+			client.Lock()
+			if link, ok := client.linkMap[id]; ok {
+				client.Unlock()
+				if content, err := client.tunnel.GetMsgContent(link); err != nil {
+					utils.PutBufPoolCopy(content)
+					s.closeClient(clientId)
+					log.Println("read msg content error", err, "close client")
+					break
+				} else {
+					if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {
+						if link.Conn != nil {
+							link.Conn.Close()
+						}
+					} else {
+						if link.UdpListener != nil && link.UdpRemoteAddr != nil {
+							link.UdpListener.WriteToUDP(content, link.UdpRemoteAddr)
+						} else {
+							link.Conn.Write(content)
+						}
+						link.Flow.Add(0, len(content))
+					}
+					utils.PutBufPoolCopy(content)
+				}
+			} else {
+				client.Unlock()
+				continue
+			}
 		}
 	}
-	return false
+
 }

+ 111 - 82
client/client.go

@@ -5,87 +5,129 @@ import (
 	"log"
 	"net"
 	"sync"
-	"sync/atomic"
 	"time"
 )
 
 type TRPClient struct {
-	svrAddr      string
-	tcpNum       int
-	connPoolSize int
-	tunnelNum    int64
-	tunnel       chan bool
-	serverStatus bool
+	svrAddr string
+	linkMap map[int]*utils.Link
+	stop    chan bool
+	tunnel  *utils.Conn
 	sync.Mutex
 	vKey string
 }
 
 //new client
-func NewRPClient(svraddr string, tcpNum int, vKey string) *TRPClient {
-	c := new(TRPClient)
-	c.svrAddr = svraddr
-	c.tcpNum = tcpNum
-	c.vKey = vKey
-	c.tunnel = make(chan bool)
-	c.connPoolSize = 5
-	return c
+func NewRPClient(svraddr string, vKey string) *TRPClient {
+	return &TRPClient{
+		svrAddr: svraddr,
+		linkMap: make(map[int]*utils.Link),
+		stop:    make(chan bool),
+		tunnel:  nil,
+		Mutex:   sync.Mutex{},
+		vKey:    vKey,
+	}
 }
 
 //start
 func (s *TRPClient) Start() error {
-	for i := 0; i < s.tcpNum; i++ {
-		go s.NewConn()
-	}
-	for i := 0; i < 5; i++ {
-		go s.dealChan()
-	}
-	go s.session()
+	s.NewConn()
 	return nil
 }
 
 //新建
-func (s *TRPClient) NewConn() error {
-	s.Lock()
-	s.serverStatus = false
+func (s *TRPClient) NewConn() {
+retry:
 	conn, err := net.Dial("tcp", s.svrAddr)
 	if err != nil {
 		log.Println("连接服务端失败,五秒后将重连")
 		time.Sleep(time.Second * 5)
-		s.Unlock()
-		go s.NewConn()
-		return err
+		goto retry
+		return
 	}
-	s.Unlock()
-	return s.processor(utils.NewConn(conn))
+	s.processor(utils.NewConn(conn))
 }
+
 //处理
-func (s *TRPClient) processor(c *utils.Conn) error {
-	s.serverStatus = true
+func (s *TRPClient) processor(c *utils.Conn) {
 	c.SetAlive()
 	if _, err := c.Write([]byte(utils.Getverifyval(s.vKey))); err != nil {
-		return err
+		return
 	}
 	c.WriteMain()
+
+	go s.dealChan()
+
 	for {
 		flags, err := c.ReadFlag()
 		if err != nil {
-			log.Println("服务端断开,五秒后将重连", err)
-			go s.NewConn()
+			log.Println("服务端断开,正在重新连接")
 			break
 		}
 		switch flags {
 		case utils.VERIFY_EER:
-			log.Fatalln("vkey:", s.vKey, "不正确,服务端拒绝连接,请检查")
-		case utils.WORK_CHAN: //隧道模式,每次开启10个,加快连接速度
+			log.Fatalf("vKey:%s不正确,服务端拒绝连接,请检查", s.vKey)
+		case utils.NEW_CONN:
+			if link, err := c.GetLinkInfo(); err != nil {
+				break
+			} else {
+				log.Println(link)
+				s.Lock()
+				s.linkMap[link.Id] = link
+				s.Unlock()
+				go s.linkProcess(link, c)
+			}
 		case utils.RES_CLOSE:
 			log.Fatal("该vkey被另一客户连接")
 		case utils.RES_MSG:
-			log.Println("服务端返回错误。")
+			log.Println("服务端返回错误,重新连接")
+			break
 		default:
-			log.Println("无法解析该错误。", flags)
+			log.Println("无法解析该错误,重新连接")
+			break
 		}
 	}
-	return nil
+	s.stop <- true
+	s.linkMap = make(map[int]*utils.Link)
+	go s.NewConn()
+}
+func (s *TRPClient) linkProcess(link *utils.Link, c *utils.Conn) {
+	//与目标建立连接
+	server, err := net.DialTimeout(link.ConnType, link.Host, time.Second*3)
+
+	if err != nil {
+		c.WriteFail(link.Id)
+		log.Println("connect to ", link.Host, "error:", err)
+		return
+	}
+
+	c.WriteSuccess(link.Id)
+
+	link.Conn = utils.NewConn(server)
+
+	for {
+		buf := utils.BufPoolCopy.Get().([]byte)
+		if n, err := server.Read(buf); err != nil {
+			utils.PutBufPoolCopy(buf)
+			s.tunnel.SendMsg([]byte(utils.IO_EOF), link)
+			break
+		} else {
+			if _, err := s.tunnel.SendMsg(buf[:n], link); err != nil {
+				utils.PutBufPoolCopy(buf)
+				c.Close()
+				break
+			}
+			utils.PutBufPoolCopy(buf)
+			if link.ConnType == utils.CONN_UDP {
+				c.Close()
+				break
+			}
+		}
+	}
+
+	s.Lock()
+	delete(s.linkMap, link.Id)
+	s.Unlock()
 }
 
 //隧道模式处理
@@ -103,50 +145,37 @@ func (s *TRPClient) dealChan() {
 		return
 	}
 	//默认长连接保持
-	c := utils.NewConn(conn)
-	c.SetAlive()
+	s.tunnel = utils.NewConn(conn)
+	s.tunnel.SetAlive()
 	//写标志
-	c.WriteChan()
-re:
-	atomic.AddInt64(&s.tunnelNum, 1)
-	//获取连接的host type(tcp or udp)
-	typeStr, host, en, de, crypt, mux, err := c.GetHostFromConn()
-	s.tunnel <- true
-	atomic.AddInt64(&s.tunnelNum, -1)
-	if err != nil {
-		c.Close()
-		return
-	}
-	s.ConnectAndCopy(c, typeStr, host, en, de, crypt, mux)
-	if mux {
-		utils.FlushConn(conn)
-		goto re
-	} else {
-		c.Close()
-	}
-}
+	s.tunnel.WriteChan()
 
-func (s *TRPClient) session() {
-	t := time.NewTicker(time.Millisecond * 1000)
-	for {
-		select {
-		case <-s.tunnel:
-		case <-t.C:
-		}
-		if s.serverStatus && s.tunnelNum < 5 {
-			go s.dealChan()
+	go func() {
+		for {
+			if id, err := s.tunnel.GetLen(); err != nil {
+				log.Println("get msg id error")
+				break
+			} else {
+				s.Lock()
+				if v, ok := s.linkMap[id]; ok {
+					s.Unlock()
+					if content, err := s.tunnel.GetMsgContent(v); err != nil {
+						log.Println("get msg content error:", err, id)
+						break
+					} else {
+						if len(content) == len(utils.IO_EOF) && string(content) == utils.IO_EOF {
+							v.Conn.Close()
+						} else if v.Conn != nil {
+							v.Conn.Write(content)
+						}
+					}
+				} else {
+					s.Unlock()
+				}
+			}
 		}
+	}()
+	select {
+	case <-s.stop:
 	}
 }
-
-func (s *TRPClient) ConnectAndCopy(c *utils.Conn, typeStr, host string, en, de int, crypt, mux bool) {
-	//与目标建立连接,超时时间为3
-	server, err := net.DialTimeout(typeStr, host, time.Second*3)
-	if err != nil {
-		log.Println("connect to ", host, "error:", err, mux)
-		c.WriteFail()
-		return
-	}
-	c.WriteSuccess()
-	utils.ReplayWaitGroup(c.Conn, server, en, de, crypt, mux, nil)
-}

+ 2 - 1
cmd/proxy_client/proxy_client.go

@@ -13,11 +13,12 @@ var (
 )
 
 func main() {
+	log.SetFlags(log.Lshortfile)
 	flag.Parse()
 	stop := make(chan int)
 	for _, v := range strings.Split(*verifyKey, ",") {
 		log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
-		go client.NewRPClient(*serverAddr, 1, v).Start()
+		go client.NewRPClient(*serverAddr, v).Start()
 	}
 	<-stop
 }

+ 1 - 2
cmd/proxy_server/proxy_server.go

@@ -19,10 +19,10 @@ var (
 	p            = flag.String("p", "", "验证密码(socks5和web)")
 	compress     = flag.String("compress", "", "数据压缩方式(snappy)")
 	crypt        = flag.String("crypt", "false", "是否加密(true|false)")
-	mux          = flag.String("mux", "false", "是否TCP多路复用(true|false)")
 )
 
 func main() {
+	log.SetFlags(log.Lshortfile)
 	flag.Parse()
 	task := &utils.Tunnel{
 		TcpPort: *httpPort,
@@ -33,7 +33,6 @@ func main() {
 			P:        *p,
 			Compress: *compress,
 			Crypt:    utils.GetBoolByStr(*crypt),
-			Mux:      utils.GetBoolByStr(*mux),
 		},
 		Flow:         &utils.Flow{},
 		UseClientCnf: false,

+ 12 - 3
conf/app.conf

@@ -9,11 +9,20 @@ runmode = dev
 #web管理密码
 password=123
 
-#http监听端口
-hostPort=80
-
 ##客户端与服务端通信端口
 tcpport=8284
 
 #web api免验证IP地址
 authip=127.0.0.1
+
+##http代理端口
+httpProxyPort=80
+
+##https代理端口
+httpsProxyPort=443
+
+##certFile绝对路径
+pemPath=/etc/nginx/certificate.crt
+
+##keyFile绝对路径
+keyPath=/etc/nginx/private.key

+ 1 - 1
conf/clients.csv

@@ -1 +1 @@
-1,wuz1nozs9dhtxic6,,true,,,0,0,,0,1
+1,wajajb6bl086s2aq,111,true,1,2,1,,20000,1000

+ 1 - 1
conf/hosts.csv

@@ -1 +1 @@
-a.o.com,127.0.0.1:8082,1,Connection:close,,
+a.o.com,127.0.0.1:8082,1,,www.baidu.com,测试

+ 4 - 1
conf/tasks.csv

@@ -1 +1,4 @@
-9001,tunnelServer,127.0.0.1:8082,,,,1,0,0,0,0,1,1,true,
+9002,tunnelServer,127.0.0.1:8082,2,2,,1,1,0,0,1,1,false,123
+53,udpServer,114.114.114.114:53,,,,1,0,0,0,2,1,true,udp测试
+9003,httpProxyServer,,,,,1,0,0,0,3,1,false,
+8024,socks5Server,,,,,1,0,0,0,4,1,true,

+ 48 - 15
server/base.go

@@ -1,29 +1,24 @@
 package server
 
 import (
+	"errors"
 	"github.com/cnlh/easyProxy/bridge"
 	"github.com/cnlh/easyProxy/utils"
+	"net"
+	"net/http"
 	"sync"
 )
 
 //server base struct
 type server struct {
-	bridge *bridge.Bridge
-	task   *utils.Tunnel
-	config *utils.Config
+	id           int
+	bridge       *bridge.Bridge
+	task         *utils.Tunnel
+	config       *utils.Config
+	errorContent []byte
 	sync.Mutex
 }
 
-func (s *server) GetTunnelAndWriteHost(connType string, clientId int, cnf *utils.Config, addr string) (link *utils.Conn, err error) {
-	if link, err = s.bridge.GetTunnel(clientId, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux); err != nil {
-		return
-	}
-	if _, err = link.WriteHost(connType, addr); err != nil {
-		link.Close()
-	}
-	return
-}
-
 func (s *server) FlowAdd(in, out int64) {
 	s.Lock()
 	defer s.Unlock()
@@ -56,7 +51,6 @@ func (s *server) ResetConfig() bool {
 			s.config.U = client.Cnf.U
 			s.config.P = client.Cnf.P
 			s.config.Compress = client.Cnf.Compress
-			s.config.Mux = client.Cnf.Mux
 			s.config.Crypt = client.Cnf.Crypt
 		}
 	} else {
@@ -64,7 +58,6 @@ func (s *server) ResetConfig() bool {
 			s.config.U = task.Config.U
 			s.config.P = task.Config.P
 			s.config.Compress = task.Config.Compress
-			s.config.Mux = task.Config.Mux
 			s.config.Crypt = task.Config.Crypt
 		}
 	}
@@ -72,3 +65,43 @@ func (s *server) ResetConfig() bool {
 	s.config.CompressDecode, s.config.CompressEncode = utils.GetCompressType(s.config.Compress)
 	return true
 }
+
+func (s *server) linkCopy(link *utils.Link, c *utils.Conn, rb []byte, tunnel *utils.Conn, flow *utils.Flow) {
+	if rb != nil {
+		if _, err := tunnel.SendMsg(rb, link); err != nil {
+			c.Close()
+			return
+		}
+		flow.Add(len(rb), 0)
+	}
+	for {
+		buf := utils.BufPoolCopy.Get().([]byte)
+		if n, err := c.Read(buf); err != nil {
+			tunnel.SendMsg([]byte(utils.IO_EOF), link)
+			break
+		} else {
+			if _, err := tunnel.SendMsg(buf[:n], link); err != nil {
+				utils.PutBufPoolCopy(buf)
+				c.Close()
+				break
+			}
+			utils.PutBufPoolCopy(buf)
+			flow.Add(n, 0)
+		}
+	}
+}
+
+func (s *server) writeConnFail(c net.Conn) {
+	c.Write([]byte(utils.ConnectionFailBytes))
+	c.Write(s.errorContent)
+}
+
+//权限认证
+func (s *server) auth(r *http.Request, c *utils.Conn, u, p string) error {
+	if u != "" && p != "" && !utils.CheckAuth(r, u, p) {
+		c.Write([]byte(utils.UnauthorizedBytes))
+		c.Close()
+		return errors.New("401 Unauthorized")
+	}
+	return nil
+}

+ 169 - 0
server/http.go

@@ -0,0 +1,169 @@
+package server
+
+import (
+	"bufio"
+	"crypto/tls"
+	"github.com/astaxie/beego"
+	"github.com/cnlh/easyProxy/bridge"
+	"github.com/cnlh/easyProxy/utils"
+	"log"
+	"net/http"
+	"net/http/httputil"
+	"strconv"
+	"sync"
+)
+
+type httpServer struct {
+	server
+	httpPort  int //http端口
+	httpsPort int //https监听端口
+	pemPath   string
+	keyPath   string
+	stop      chan bool
+}
+
+func NewHttp(bridge *bridge.Bridge, c *utils.Tunnel) *httpServer {
+	httpPort, _ := beego.AppConfig.Int("httpProxyPort")
+	httpsPort, _ := beego.AppConfig.Int("httpsProxyPort")
+	pemPath := beego.AppConfig.String("pemPath")
+	keyPath := beego.AppConfig.String("keyPath")
+	return &httpServer{
+		server: server{
+			task:   c,
+			bridge: bridge,
+			Mutex:  sync.Mutex{},
+		},
+		httpPort:  httpPort,
+		httpsPort: httpsPort,
+		pemPath:   pemPath,
+		keyPath:   keyPath,
+		stop:      make(chan bool),
+	}
+}
+
+func (s *httpServer) Start() error {
+	var err error
+	var http, https *http.Server
+	if s.errorContent, err = utils.ReadAllFromFile(beego.AppPath + "/web/static/page/error.html"); err != nil {
+		s.errorContent = []byte("easyProxy 404")
+	}
+
+	if s.httpPort > 0 {
+		http = s.NewServer(s.httpPort)
+		go func() {
+			log.Println("启动http监听,端口为", s.httpPort)
+			err := http.ListenAndServe()
+			if err != nil {
+				log.Fatalln(err)
+			}
+		}()
+	}
+	if s.httpsPort > 0 {
+		https = s.NewServer(s.httpsPort)
+		go func() {
+			log.Println("启动https监听,端口为", s.httpsPort)
+			err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
+			if err != nil {
+				log.Fatalln(err)
+			}
+		}()
+	}
+	select {
+	case <-s.stop:
+		if http != nil {
+			http.Close()
+		}
+		if https != nil {
+			https.Close()
+		}
+	}
+	return nil
+}
+
+func (s *httpServer) Close() {
+	s.stop <- true
+}
+
+func (s *httpServer) handleTunneling(w http.ResponseWriter, r *http.Request) {
+	hijacker, ok := w.(http.Hijacker)
+	if !ok {
+		http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
+		return
+	}
+	conn, _, err := hijacker.Hijack()
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusServiceUnavailable)
+	}
+	s.process(utils.NewConn(conn), r)
+}
+
+func (s *httpServer) process(c *utils.Conn, r *http.Request) {
+	//多客户端域名代理
+	var (
+		isConn = true
+		link   *utils.Link
+		host   *utils.Host
+		tunnel *utils.Conn
+		err    error
+	)
+	for {
+		//首次获取conn
+		if isConn {
+			if host, err = GetInfoByHost(r.Host); err != nil {
+				log.Printf("the host %s is not found !", r.Host)
+				break
+			}
+			//流量限制
+			if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
+				break
+			}
+			host.Client.Cnf.CompressDecode, host.Client.Cnf.CompressEncode = utils.GetCompressType(host.Client.Cnf.Compress)
+			//权限控制
+			if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
+				break
+			}
+			link = utils.NewLink(host.Client.GetId(), utils.CONN_TCP, host.Target, host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil)
+			if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, link); err != nil {
+				break
+			}
+			isConn = false
+		} else {
+			r, err = http.ReadRequest(bufio.NewReader(c))
+			if err != nil {
+				log.Println(err)
+				break
+			}
+		}
+		//根据设定,修改header和host
+		utils.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String())
+		b, err := httputil.DumpRequest(r, true)
+		if err != nil {
+			break
+		}
+		host.Flow.Add(len(b), 0)
+		if _, err := tunnel.SendMsg(b, link); err != nil {
+			c.Close()
+			break
+		}
+	}
+
+	if isConn {
+		s.writeConnFail(c.Conn)
+	} else {
+		tunnel.SendMsg([]byte(utils.IO_EOF), link)
+	}
+
+	c.Close()
+
+}
+
+func (s *httpServer) NewServer(port int) *http.Server {
+	return &http.Server{
+		Addr: ":" + strconv.Itoa(port),
+		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			s.handleTunneling(w, r)
+		}),
+		// Disable HTTP/2.
+		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
+	}
+}

+ 0 - 111
server/process.go

@@ -1,111 +0,0 @@
-package server
-
-import (
-	"bufio"
-	"github.com/cnlh/easyProxy/utils"
-	"github.com/pkg/errors"
-	"log"
-	"net/http"
-	"net/http/httputil"
-	"sync"
-)
-
-type process func(c *utils.Conn, s *TunnelModeServer) error
-
-//tcp隧道模式
-func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error {
-	if !s.ResetConfig() {
-		c.Close()
-		return errors.New("流量超出")
-	}
-	return s.dealClient(c, s.config, s.task.Target, "", nil)
-}
-
-//http代理模式
-func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error {
-	if !s.ResetConfig() {
-		c.Close()
-		return errors.New("流量超出")
-	}
-	method, addr, rb, err, r := c.GetHost()
-	if err != nil {
-		log.Println(err)
-		c.Close()
-		return err
-	}
-	if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
-		return err
-	}
-	return s.dealClient(c, s.config, addr, method, rb)
-}
-
-//多客户端域名代理
-func ProcessHost(c *utils.Conn, s *TunnelModeServer) error {
-	var (
-		isConn = true
-		link   *utils.Conn
-		host   *utils.Host
-		wg     sync.WaitGroup
-	)
-	for {
-		r, err := http.ReadRequest(bufio.NewReader(c))
-		if err != nil {
-			break
-		}
-		//首次获取conn
-		if isConn {
-			if host, err = GetInfoByHost(r.Host); err != nil {
-				log.Printf("the host %s is not found !", r.Host)
-				break
-			}
-			//流量限制
-			if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
-				break
-			}
-			host.Client.Cnf.CompressDecode, host.Client.Cnf.CompressEncode = utils.GetCompressType(host.Client.Cnf.Compress)
-			//权限控制
-			if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
-				break
-			}
-			if link, err = s.GetTunnelAndWriteHost(utils.CONN_TCP, host.Client.Id, host.Client.Cnf, host.Target); err != nil {
-				log.Println("get bridge tunnel error: ", err)
-				break
-			}
-			if flag, err := link.ReadFlag(); err != nil || flag == utils.CONN_ERROR {
-				log.Printf("the host %s connection to %s error", r.Host, host.Target)
-				break
-			} else {
-				wg.Add(1)
-				go func() {
-					out, _ := utils.Relay(c.Conn, link.Conn, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, host.Client.Cnf.Mux, host.Client.Rate)
-					wg.Done()
-					s.FlowAddHost(host, 0, out)
-				}()
-			}
-			isConn = false
-		}
-		//根据设定,修改header和host
-		utils.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String())
-		b, err := httputil.DumpRequest(r, true)
-		if err != nil {
-			break
-		}
-		s.FlowAddHost(host, int64(len(b)), 0)
-		if _, err := link.WriteTo(b, host.Client.Cnf.CompressEncode, host.Client.Cnf.Crypt, host.Client.Rate); err != nil {
-			break
-		}
-	}
-	wg.Wait()
-	if host != nil && host.Client.Cnf != nil && host.Client.Cnf.Mux && link != nil {
-		link.WriteTo([]byte(utils.IO_EOF), host.Client.Cnf.CompressEncode, host.Client.Cnf.Crypt, host.Client.Rate)
-		s.bridge.ReturnTunnel(link, host.Client.Id)
-	} else if link != nil {
-		link.Close()
-	}
-
-	if isConn {
-		s.writeConnFail(c.Conn)
-	}
-	c.Close()
-	return nil
-}

+ 8 - 20
server/server.go

@@ -2,27 +2,17 @@ package server
 
 import (
 	"errors"
-	"github.com/astaxie/beego"
 	"github.com/cnlh/easyProxy/bridge"
 	"github.com/cnlh/easyProxy/utils"
 	"log"
 	"reflect"
 	"strings"
-	"sync"
 )
 
-type RunServer struct {
-	flag       int   //标志
-	ExportFlow int64 //出口流量
-	InletFlow  int64 //入口流量
-	service    interface{}
-	sync.Mutex
-}
-
 var (
-	Bridge    *bridge.Bridge
-	RunList   map[int]interface{} //运行中的任务
-	CsvDb     = utils.GetCsvDb()
+	Bridge  *bridge.Bridge
+	RunList map[int]interface{} //运行中的任务
+	CsvDb   = utils.GetCsvDb()
 )
 
 func init() {
@@ -69,9 +59,8 @@ func NewMode(Bridge *bridge.Bridge, c *utils.Tunnel) interface{} {
 		return NewUdpModeServer(Bridge, c)
 	case "webServer":
 		InitFromCsv()
-		p, _ := beego.AppConfig.Int("hostPort")
 		t := &utils.Tunnel{
-			TcpPort: p,
+			TcpPort: 0,
 			Mode:    "httpHostServer",
 			Target:  "",
 			Config:  &utils.Config{},
@@ -82,7 +71,7 @@ func NewMode(Bridge *bridge.Bridge, c *utils.Tunnel) interface{} {
 	case "hostServer":
 		return NewHostServer(c)
 	case "httpHostServer":
-		return NewTunnelModeServer(ProcessHost, Bridge, c)
+		return NewHttp(Bridge, c)
 	}
 	return nil
 }
@@ -161,7 +150,7 @@ func GetTunnel(start, length int, typeVal string, clientId int) ([]*utils.Tunnel
 			continue
 		}
 		cnt++
-		if _, ok := Bridge.SignalList[v.Client.Id]; ok {
+		if _, ok := Bridge.Client[v.Client.Id]; ok {
 			v.Client.IsConnect = true
 		} else {
 			v.Client.IsConnect = false
@@ -189,7 +178,7 @@ func GetClientList(start, length int) (list []*utils.Client, cnt int) {
 
 func dealClientData(list []*utils.Client) {
 	for _, v := range list {
-		if _, ok := Bridge.SignalList[v.Id]; ok {
+		if _, ok := Bridge.Client[v.Id]; ok {
 			v.IsConnect = true
 		} else {
 			v.IsConnect = false
@@ -228,8 +217,7 @@ func DelTunnelAndHostByClientId(clientId int) {
 
 //关闭客户端连接
 func DelClientConnect(clientId int) {
-	Bridge.DelClientTunnel(clientId)
-	Bridge.DelClientSignal(clientId)
+	Bridge.DelClient(clientId)
 }
 
 func GetDashboardData() map[string]int {

+ 12 - 36
server/socks5.go

@@ -106,7 +106,7 @@ func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) {
 }
 
 //do conn
-func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils.Conn, err error) {
+func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
 	addrType := make([]byte, 1)
 	c.Read(addrType)
 	var host string
@@ -127,8 +127,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils
 		host = string(domain)
 	default:
 		s.sendReply(c, addrTypeNotSupported)
-		err = errors.New("Address type not supported")
-		return nil, err
+		return
 	}
 
 	var port uint16
@@ -141,34 +140,22 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *utils
 	} else {
 		ltype = utils.CONN_TCP
 	}
-	if proxyConn, err = s.GetTunnelAndWriteHost(ltype, s.task.Client.Id, s.config, addr); err != nil {
-		log.Println("get bridge tunnel error: ", err)
+	link := utils.NewLink(s.task.Client.GetId(), ltype, addr, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, utils.NewConn(c), s.task.Flow, nil, s.task.Client.Rate, nil)
+
+	if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
+		log.Println("error", err, link)
+		c.Close()
 		return
-	}
-	s.sendReply(c, succeeded)
-	var flag string
-	if flag, err = proxyConn.ReadFlag(); err == nil {
-		if flag != utils.CONN_SUCCESS {
-			err = errors.New("conn failed")
-		}
+	} else {
+		s.sendReply(c, succeeded)
+		s.linkCopy(link, utils.NewConn(c), nil, tunnel, s.task.Flow)
 	}
 	return
 }
 
 //conn
 func (s *Sock5ModeServer) handleConnect(c net.Conn) {
-	proxyConn, err := s.doConnect(c, connectMethod)
-	defer func() {
-		if s.config.Mux && proxyConn != nil {
-			s.bridge.ReturnTunnel(proxyConn, s.task.Client.Id)
-		}
-	}()
-	if err != nil {
-		c.Close()
-	} else {
-		out, in := utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux, s.task.Client.Rate)
-		s.FlowAdd(in, out)
-	}
+	s.doConnect(c, connectMethod)
 }
 
 // passive mode
@@ -195,18 +182,7 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
 		c.Read(dummy)
 	}
 
-	proxyConn, err := s.doConnect(c, associateMethod)
-	defer func() {
-		if s.config.Mux && proxyConn != nil {
-			s.bridge.ReturnTunnel(proxyConn, s.task.Client.Id)
-		}
-	}()
-	if err != nil {
-		c.Close()
-	} else {
-		out, in := utils.ReplayWaitGroup(proxyConn.Conn, c, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux, s.task.Client.Rate)
-		s.FlowAdd(in, out)
-	}
+	s.doConnect(c, associateMethod)
 }
 
 //new conn

+ 37 - 43
server/tcp.go

@@ -2,21 +2,18 @@ package server
 
 import (
 	"errors"
-	"fmt"
 	"github.com/astaxie/beego"
 	"github.com/cnlh/easyProxy/bridge"
 	"github.com/cnlh/easyProxy/utils"
 	"log"
 	"net"
-	"net/http"
 	"strings"
 )
 
 type TunnelModeServer struct {
 	server
-	errorContent []byte
-	process      process
-	listener     *net.TCPListener
+	process  process
+	listener *net.TCPListener
 }
 
 //tcp|http|host
@@ -32,9 +29,6 @@ func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *utils.Tun
 //开始
 func (s *TunnelModeServer) Start() error {
 	var err error
-	if s.errorContent, err = utils.ReadAllFromFile(beego.AppPath + "/web/static/page/error.html"); err != nil {
-		s.errorContent = []byte("easyProxy 404")
-	}
 	s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.task.TcpPort, ""})
 	if err != nil {
 		return err
@@ -53,44 +47,15 @@ func (s *TunnelModeServer) Start() error {
 	return nil
 }
 
-//权限认证
-func (s *TunnelModeServer) auth(r *http.Request, c *utils.Conn, u, p string) error {
-	if u != "" && p != "" && !utils.CheckAuth(r, u, p) {
-		c.Write([]byte(utils.UnauthorizedBytes))
-		c.Close()
-		return errors.New("401 Unauthorized")
-	}
-	return nil
-}
-
-func (s *TunnelModeServer) writeConnFail(c net.Conn) {
-	c.Write([]byte(utils.ConnectionFailBytes))
-	c.Write(s.errorContent)
-}
-
 //与客户端建立通道
 func (s *TunnelModeServer) dealClient(c *utils.Conn, cnf *utils.Config, addr string, method string, rb []byte) error {
-	var link *utils.Conn
-	var err error
-	defer func() {
-		if cnf.Mux && link != nil {
-			s.bridge.ReturnTunnel(link, s.task.Client.Id)
-		}
-	}()
-	if link, err = s.GetTunnelAndWriteHost(utils.CONN_TCP, s.task.Client.Id, cnf, addr); err != nil {
-		log.Println("get bridge tunnel error: ", err)
+	link := utils.NewLink(s.task.Client.GetId(), utils.CONN_TCP, addr, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, c, s.task.Flow, nil, s.task.Client.Rate, nil)
+
+	if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
+		c.Close()
 		return err
-	}
-	if flag, err := link.ReadFlag(); err == nil {
-		if flag == utils.CONN_SUCCESS {
-			if method == "CONNECT" {
-				fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
-			} else if rb != nil {
-				link.WriteTo(rb, cnf.CompressEncode, cnf.Crypt, s.task.Client.Rate)
-			}
-			out, in := utils.ReplayWaitGroup(link.Conn, c.Conn, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, cnf.Mux, s.task.Client.Rate)
-			s.FlowAdd(in, out)
-		}
+	} else {
+		s.linkCopy(link, c, rb, tunnel, s.task.Flow)
 	}
 	return nil
 }
@@ -142,3 +107,32 @@ func NewHostServer(task *utils.Tunnel) *HostServer {
 func (s *HostServer) Close() error {
 	return nil
 }
+
+type process func(c *utils.Conn, s *TunnelModeServer) error
+
+//tcp隧道模式
+func ProcessTunnel(c *utils.Conn, s *TunnelModeServer) error {
+	if !s.ResetConfig() {
+		c.Close()
+		return errors.New("流量超出")
+	}
+	return s.dealClient(c, s.config, s.task.Target, "", nil)
+}
+
+//http代理模式
+func ProcessHttp(c *utils.Conn, s *TunnelModeServer) error {
+	if !s.ResetConfig() {
+		c.Close()
+		return errors.New("流量超出")
+	}
+	method, addr, rb, err, r := c.GetHost()
+	if err != nil {
+		log.Println(err)
+		c.Close()
+		return err
+	}
+	if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
+		return err
+	}
+	return s.dealClient(c, s.config, addr, method, rb)
+}

+ 9 - 34
server/udp.go

@@ -3,8 +3,6 @@ package server
 import (
 	"github.com/cnlh/easyProxy/bridge"
 	"github.com/cnlh/easyProxy/utils"
-	"io"
-	"log"
 	"net"
 	"strings"
 )
@@ -31,9 +29,9 @@ func (s *UdpModeServer) Start() error {
 	if err != nil {
 		return err
 	}
-	data := make([]byte, 1472) //udp数据包大小
+	buf := utils.BufPoolUdp.Get().([]byte)
 	for {
-		n, addr, err := s.listener.ReadFromUDP(data)
+		n, addr, err := s.listener.ReadFromUDP(buf)
 		if err != nil {
 			if strings.Contains(err.Error(), "use of closed network connection") {
 				break
@@ -43,42 +41,19 @@ func (s *UdpModeServer) Start() error {
 		if !s.ResetConfig() {
 			continue
 		}
-		go s.process(addr, data[:n])
+		go s.process(addr, buf[:n])
 	}
 	return nil
 }
 
-//TODO:效率问题有待解决--->建立稳定通道,重复利用,提高效率,下个版本
 func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
-	conn, err := s.bridge.GetTunnel(s.task.Client.Id, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, s.config.Mux)
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	if _, err := conn.WriteHost(utils.CONN_UDP, s.task.Target); err != nil {
-		conn.Close()
+	link := utils.NewLink(s.task.Client.GetId(), utils.CONN_UDP, s.task.Target, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, nil, s.task.Flow, s.listener, s.task.Client.Rate, addr)
+
+	if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
 		return
-	}
-	if flag, err := conn.ReadFlag(); err == nil {
-		defer func() {
-			if conn != nil && s.config.Mux {
-				conn.WriteTo([]byte(utils.IO_EOF), s.config.CompressEncode, s.config.Crypt, s.task.Client.Rate)
-				s.bridge.ReturnTunnel(conn, s.task.Client.Id)
-			} else {
-				conn.Close()
-			}
-		}()
-		if flag == utils.CONN_SUCCESS {
-			in, _ := conn.WriteTo(data, s.config.CompressEncode, s.config.Crypt, s.task.Client.Rate)
-			buf := utils.BufPoolUdp.Get().([]byte)
-			out, err := conn.ReadFrom(buf, s.config.CompressDecode, s.config.Crypt, s.task.Client.Rate)
-			if err != nil || err == io.EOF {
-				return
-			}
-			s.listener.WriteToUDP(buf[:out], addr)
-			s.FlowAdd(int64(in), int64(out))
-			utils.BufPoolUdp.Put(buf)
-		}
+	} else {
+		s.task.Flow.Add(len(data), 0)
+		tunnel.SendMsg(data, link)
 	}
 }
 

+ 151 - 113
utils/conn.go

@@ -13,6 +13,7 @@ import (
 	"net/url"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -52,12 +53,6 @@ func (s *CryptConn) Write(b []byte) (n int, err error) {
 
 //解密读
 func (s *CryptConn) Read(b []byte) (n int, err error) {
-	defer func() {
-		if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
-			err = io.EOF
-			n = 0
-		}
-	}()
 	var lens int
 	var buf []byte
 	var rb []byte
@@ -122,14 +117,8 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) {
 
 //snappy压缩读 包含解密
 func (s *SnappyConn) Read(b []byte) (n int, err error) {
-	buf := bufPool.Get().([]byte)
-	defer func() {
-		if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
-			err = io.EOF
-			n = 0
-		}
-		bufPool.Put(buf)
-	}()
+	buf := BufPool.Get().([]byte)
+	defer BufPool.Put(buf)
 	if n, err = s.r.Read(buf); err != nil {
 		return
 	}
@@ -152,6 +141,7 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
 
 type Conn struct {
 	Conn net.Conn
+	sync.Mutex
 }
 
 //new conn
@@ -161,6 +151,36 @@ func NewConn(conn net.Conn) *Conn {
 	return c
 }
 
+//从tcp报文中解析出host,连接类型等
+func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
+	var b [32 * 1024]byte
+	var n int
+	if n, err = s.Read(b[:]); err != nil {
+		return
+	}
+	rb = b[:n]
+	r, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(rb)))
+	if err != nil {
+		return
+	}
+	hostPortURL, err := url.Parse(r.Host)
+	if err != nil {
+		address = r.Host
+		err = nil
+		return
+	}
+	if hostPortURL.Opaque == "443" { //https访问
+		address = r.Host + ":443"
+	} else { //http访问
+		if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80
+			address = r.Host + ":80"
+		} else {
+			address = r.Host
+		}
+	}
+	return
+}
+
 //读取指定长度内容
 func (s *Conn) ReadLen(cLen int) ([]byte, error) {
 	if cLen > poolSize {
@@ -168,11 +188,11 @@ func (s *Conn) ReadLen(cLen int) ([]byte, error) {
 	}
 	var buf []byte
 	if cLen <= poolSizeSmall {
-		buf = bufPoolSmall.Get().([]byte)[:cLen]
-		defer bufPoolSmall.Put(buf)
+		buf = BufPoolSmall.Get().([]byte)[:cLen]
+		defer BufPoolSmall.Put(buf)
 	} else {
-		buf = bufPoolMax.Get().([]byte)[:cLen]
-		defer bufPoolMax.Put(buf)
+		buf = BufPoolMax.Get().([]byte)[:cLen]
+		defer BufPoolMax.Put(buf)
 	}
 	if n, err := io.ReadFull(s, buf); err != nil || n != cLen {
 		return buf, errors.New("读取指定长度错误" + err.Error())
@@ -180,7 +200,7 @@ func (s *Conn) ReadLen(cLen int) ([]byte, error) {
 	return buf, nil
 }
 
-//获取长度
+//read length or id (content length=4)
 func (s *Conn) GetLen() (int, error) {
 	val, err := s.ReadLen(4)
 	if err != nil {
@@ -189,17 +209,7 @@ func (s *Conn) GetLen() (int, error) {
 	return GetLenByBytes(val)
 }
 
-//写入长度+内容 粘包
-func (s *Conn) WriteLen(buf []byte) (int, error) {
-	var b []byte
-	var err error
-	if b, err = GetLenBytes(buf); err != nil {
-		return 0, err
-	}
-	return s.Write(b)
-}
-
-//读取flag
+//read flag
 func (s *Conn) ReadFlag() (string, error) {
 	val, err := s.ReadLen(4)
 	if err != nil {
@@ -208,41 +218,21 @@ func (s *Conn) ReadFlag() (string, error) {
 	return string(val), err
 }
 
-//读取host 连接地址 压缩类型
-func (s *Conn) GetHostFromConn() (typeStr string, host string, en, de int, crypt, mux bool, err error) {
-retry:
-	lType, err := s.ReadLen(3)
+//read connect status
+func (s *Conn) GetConnStatus() (id int, status bool, err error) {
+	id, err = s.GetLen()
 	if err != nil {
 		return
 	}
-	if typeStr = string(lType); typeStr == TEST_FLAG {
-		en, de, crypt, mux = s.GetConnInfoFromConn()
-		goto retry
-	} else if typeStr != CONN_TCP && typeStr != CONN_UDP {
-		err = errors.New("unknown conn type")
-		return
-	}
-	cLen, err := s.GetLen()
-	if err != nil || cLen > poolSize {
-		return
-	}
-	hostByte, err := s.ReadLen(cLen)
-	if err != nil {
+	var b []byte
+	if b, err = s.ReadLen(1); err != nil {
 		return
+	} else {
+		status = GetBoolByStr(string(b[0]))
 	}
-	host = string(hostByte)
 	return
 }
 
-//写连接类型 和 host地址
-func (s *Conn) WriteHost(ltype string, host string) (int, error) {
-	raw := bytes.NewBuffer([]byte{})
-	binary.Write(raw, binary.LittleEndian, []byte(ltype))
-	binary.Write(raw, binary.LittleEndian, int32(len([]byte(host))))
-	binary.Write(raw, binary.LittleEndian, []byte(host))
-	return s.Write(raw.Bytes())
-}
-
 //设置连接为长连接
 func (s *Conn) SetAlive() {
 	conn := s.Conn.(*net.TCPConn)
@@ -251,40 +241,11 @@ func (s *Conn) SetAlive() {
 	conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
 }
 
+//set read dead time
 func (s *Conn) SetReadDeadline(t time.Duration) {
 	s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
 }
 
-//从tcp报文中解析出host,连接类型等 TODO 多种情况
-func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
-	var b [32 * 1024]byte
-	var n int
-	if n, err = s.Read(b[:]); err != nil {
-		return
-	}
-	rb = b[:n]
-	r, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(rb)))
-	if err != nil {
-		return
-	}
-	hostPortURL, err := url.Parse(r.Host)
-	if err != nil {
-		address = r.Host
-		err = nil
-		return
-	}
-	if hostPortURL.Opaque == "443" { //https访问
-		address = r.Host + ":443"
-	} else { //http访问
-		if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80
-			address = r.Host + ":80"
-		} else {
-			address = r.Host
-		}
-	}
-	return
-}
-
 //单独读(加密|压缩)
 func (s *Conn) ReadFrom(b []byte, compress int, crypt bool, rate *Rate) (int, error) {
 	if COMPRESS_SNAPY_DECODE == compress {
@@ -301,24 +262,112 @@ func (s *Conn) WriteTo(b []byte, compress int, crypt bool, rate *Rate) (n int, e
 	return NewCryptConn(s.Conn, crypt, rate).Write(b)
 }
 
-//写压缩方式,加密
-func (s *Conn) WriteConnInfo(en, de int, crypt, mux bool) {
-	s.Write([]byte(strconv.Itoa(en) + strconv.Itoa(de) + GetStrByBool(crypt) + GetStrByBool(mux)))
+//send msg
+func (s *Conn) SendMsg(content []byte, link *Link) (n int, err error) {
+	/*
+		The msg info is formed as follows:
+		+----+--------+
+		|id | content |
+		+----+--------+
+		| 4  |  ...   |
+		+----+--------+
+*/
+	s.Lock()
+	defer s.Unlock()
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, int32(link.Id))
+	if n, err = s.Write(raw.Bytes()); err != nil {
+		return
+	}
+	raw.Reset()
+	binary.Write(raw, binary.LittleEndian, content)
+	n, err = s.WriteTo(raw.Bytes(), link.En, link.Crypt, link.Rate)
+	return
 }
 
-//获取压缩方式,是否加密
-func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
-	buf, err := s.ReadLen(4)
-	if err != nil {
+//get msg content from conn
+func (s *Conn) GetMsgContent(link *Link) (content []byte, err error) {
+	s.Lock()
+	defer s.Unlock()
+	buf := BufPoolCopy.Get().([]byte)
+	if n, err := s.ReadFrom(buf, link.De, link.Crypt, link.Rate); err == nil && n > 4 {
+		content = buf[:n]
+	}
+	return
+}
+
+//send info for link
+func (s *Conn) SendLinkInfo(link *Link) (int, error) {
+	/*
+		The  link info is formed as follows:
+		+----------+------+----------+------+----------+-----+
+		| id | len | type |  hostlen | host | en | de |crypt |
+		+----------+------+----------+------+---------+------+
+		| 4  |  4  |  3   |     4    | host | 1  | 1  |   1  |
+		+----------+------+----------+------+----+----+------+
+	*/
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, []byte(NEW_CONN))
+	binary.Write(raw, binary.LittleEndian, int32(14+len(link.Host)))
+	binary.Write(raw, binary.LittleEndian, int32(link.Id))
+	binary.Write(raw, binary.LittleEndian, []byte(link.ConnType))
+	binary.Write(raw, binary.LittleEndian, int32(len(link.Host)))
+	binary.Write(raw, binary.LittleEndian, []byte(link.Host))
+	binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.En)))
+	binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.De)))
+	binary.Write(raw, binary.LittleEndian, []byte(GetStrByBool(link.Crypt)))
+	s.Lock()
+	defer s.Unlock()
+	return s.Write(raw.Bytes())
+}
+
+func (s *Conn) GetLinkInfo() (link *Link, err error) {
+	s.Lock()
+	defer s.Unlock()
+	var hostLen, n int
+	var buf []byte
+	if n, err = s.GetLen(); err != nil {
+		return
+	}
+	link = new(Link)
+	if buf, err = s.ReadLen(n); err != nil {
+		return
+	}
+	if link.Id, err = GetLenByBytes(buf[:4]); err != nil {
 		return
 	}
-	en, _ = strconv.Atoi(string(buf[0]))
-	de, _ = strconv.Atoi(string(buf[1]))
-	crypt = GetBoolByStr(string(buf[2]))
-	mux = GetBoolByStr(string(buf[3]))
+	link.ConnType = string(buf[4:7])
+	if hostLen, err = GetLenByBytes(buf[7:11]); err != nil {
+		return
+	} else {
+		link.Host = string(buf[11 : 11+hostLen])
+		link.En = GetIntNoErrByStr(string(buf[11+hostLen]))
+		link.De = GetIntNoErrByStr(string(buf[12+hostLen]))
+		link.Crypt = GetBoolByStr(string(buf[13+hostLen]))
+	}
 	return
 }
 
+//write connect success
+func (s *Conn) WriteSuccess(id int) (int, error) {
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, int32(id))
+	binary.Write(raw, binary.LittleEndian, []byte("1"))
+	s.Lock()
+	defer s.Unlock()
+	return s.Write(raw.Bytes())
+}
+
+//write connect fail
+func (s *Conn) WriteFail(id int) (int, error) {
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, int32(id))
+	binary.Write(raw, binary.LittleEndian, []byte("0"))
+	s.Lock()
+	defer s.Unlock()
+	return s.Write(raw.Bytes())
+}
+
 //close
 func (s *Conn) Close() error {
 	return s.Conn.Close()
@@ -351,29 +400,18 @@ func (s *Conn) WriteClose() (int, error) {
 
 //write main
 func (s *Conn) WriteMain() (int, error) {
+	s.Lock()
+	defer s.Unlock()
 	return s.Write([]byte(WORK_MAIN))
 }
 
 //write chan
 func (s *Conn) WriteChan() (int, error) {
+	s.Lock()
+	defer s.Unlock()
 	return s.Write([]byte(WORK_CHAN))
 }
 
-//write test
-func (s *Conn) WriteTest() (int, error) {
-	return s.Write([]byte(TEST_FLAG))
-}
-
-//write test
-func (s *Conn) WriteSuccess() (int, error) {
-	return s.Write([]byte(CONN_SUCCESS))
-}
-
-//write test
-func (s *Conn) WriteFail() (int, error) {
-	return s.Write([]byte(CONN_ERROR))
-}
-
 //获取长度+内容
 func GetLenBytes(buf []byte) (b []byte, err error) {
 	raw := bytes.NewBuffer([]byte{})

+ 17 - 82
utils/file.go

@@ -17,61 +17,8 @@ var (
 	once  sync.Once
 )
 
-type Flow struct {
-	ExportFlow int64 //出口流量
-	InletFlow  int64 //入口流量
-	FlowLimit  int64 //流量限制,出口+入口 /M
-}
-
-type Client struct {
-	Cnf       *Config
-	Id        int    //id
-	VerifyKey string //验证密钥
-	Addr      string //客户端ip地址
-	Remark    string //备注
-	Status    bool   //是否开启
-	IsConnect bool   //是否连接
-	RateLimit int    //速度限制 /kb
-	Flow      *Flow  //流量
-	Rate      *Rate  //速度控制
-}
-
-type Tunnel struct {
-	Id           int     //Id
-	TcpPort      int     //服务端与客户端通信端口
-	Mode         string  //启动方式
-	Target       string  //目标
-	Status       bool    //是否开启
-	Client       *Client //所属客户端id
-	Flow         *Flow
-	Config       *Config
-	UseClientCnf bool   //是否继承客户端配置
-	Remark       string //备注
-}
-
-type Config struct {
-	U              string //socks5验证用户名
-	P              string //socks5验证密码
-	Compress       string //压缩方式
-	Crypt          bool   //是否加密
-	Mux            bool   //是否加密
-	CompressEncode int    //加密方式
-	CompressDecode int    //解密方式
-}
-
-type Host struct {
-	Host         string //启动方式
-	Target       string //目标
-	HeaderChange string //host修改
-	HostChange   string //host修改
-	Flow         *Flow
-	Client       *Client
-	Remark       string //备注
-}
-
 func NewCsv() *Csv {
-	c := new(Csv)
-	return c
+	return new(Csv)
 }
 
 type Csv struct {
@@ -108,7 +55,6 @@ func (s *Csv) StoreTasksToCsv() {
 			task.Config.Compress,
 			utils.GetStrByBool(task.Status),
 			GetStrByBool(task.Config.Crypt),
-			GetStrByBool(task.Config.Mux),
 			strconv.Itoa(task.Config.CompressEncode),
 			strconv.Itoa(task.Config.CompressDecode),
 			strconv.Itoa(task.Id),
@@ -160,17 +106,16 @@ func (s *Csv) LoadTaskFromCsv() {
 				P:              item[4],
 				Compress:       item[5],
 				Crypt:          GetBoolByStr(item[7]),
-				Mux:            GetBoolByStr(item[8]),
-				CompressEncode: GetIntNoErrByStr(item[9]),
-				CompressDecode: GetIntNoErrByStr(item[10]),
+				CompressEncode: GetIntNoErrByStr(item[8]),
+				CompressDecode: GetIntNoErrByStr(item[9]),
 			},
 			Status:       utils.GetBoolByStr(item[6]),
-			Id:           GetIntNoErrByStr(item[11]),
-			UseClientCnf: GetBoolByStr(item[13]),
-			Remark:       item[14],
+			Id:           GetIntNoErrByStr(item[10]),
+			UseClientCnf: GetBoolByStr(item[12]),
+			Remark:       item[13],
 		}
 		post.Flow = new(Flow)
-		if post.Client, err = s.GetClient(GetIntNoErrByStr(item[12])); err != nil {
+		if post.Client, err = s.GetClient(GetIntNoErrByStr(item[11])); err != nil {
 			continue
 		}
 		tasks = append(tasks, post)
@@ -284,13 +229,12 @@ func (s *Csv) LoadClientFromCsv() {
 			VerifyKey: item[1],
 			Remark:    item[2],
 			Status:    GetBoolByStr(item[3]),
-			RateLimit: GetIntNoErrByStr(item[9]),
+			RateLimit: GetIntNoErrByStr(item[8]),
 			Cnf: &Config{
 				U:        item[4],
 				P:        item[5],
 				Crypt:    GetBoolByStr(item[6]),
-				Mux:      GetBoolByStr(item[7]),
-				Compress: item[8],
+				Compress: item[7],
 			},
 		}
 		if post.Id > s.ClientIncreaseId {
@@ -301,7 +245,7 @@ func (s *Csv) LoadClientFromCsv() {
 			post.Rate.Start()
 		}
 		post.Flow = new(Flow)
-		post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[10]))
+		post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[9]))
 		clients = append(clients, post)
 	}
 	s.Clients = clients
@@ -407,10 +351,14 @@ func (s *Csv) GetClientId() int {
 func (s *Csv) UpdateClient(t *Client) error {
 	s.Lock()
 	defer s.Unlock()
-	for k, v := range s.Clients {
+	for _, v := range s.Clients {
 		if v.Id == t.Id {
-			s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
-			s.Clients = append(s.Clients, t)
+			v.Cnf = t.Cnf
+			v.VerifyKey = t.VerifyKey
+			v.Remark = t.Remark
+			v.RateLimit = t.RateLimit
+			v.Flow = t.Flow
+			v.Rate = t.Rate
 			s.StoreClientsToCsv()
 			return nil
 		}
@@ -458,7 +406,6 @@ func (s *Csv) StoreClientsToCsv() {
 			client.Cnf.U,
 			client.Cnf.P,
 			utils.GetStrByBool(client.Cnf.Crypt),
-			utils.GetStrByBool(client.Cnf.Mux),
 			client.Cnf.Compress,
 			strconv.Itoa(client.RateLimit),
 			strconv.Itoa(int(client.Flow.FlowLimit)),
@@ -480,15 +427,3 @@ func GetCsvDb() *Csv {
 	return CsvDb
 }
 
-//深拷贝Tunnel
-func DeepCopyConfig(c *Config) *Config {
-	return &Config{
-		U:              c.U,
-		P:              c.P,
-		Compress:       c.Compress,
-		Crypt:          c.Crypt,
-		Mux:            c.Mux,
-		CompressEncode: c.CompressEncode,
-		CompressDecode: c.CompressDecode,
-	}
-}

+ 116 - 0
utils/link.go

@@ -0,0 +1,116 @@
+package utils
+
+import (
+	"net"
+	"sync"
+)
+
+type Link struct {
+	Id            int    //id
+	ConnType      string //连接类型
+	Host          string //目标
+	En            int    //加密
+	De            int    //解密
+	Crypt         bool   //加密
+	Conn          *Conn
+	Flow          *Flow
+	UdpListener   *net.UDPConn
+	Rate          *Rate
+	UdpRemoteAddr *net.UDPAddr
+}
+
+func NewLink(id int, connType string, host string, en, de int, crypt bool, conn *Conn, flow *Flow, udpListener *net.UDPConn, rate *Rate, UdpRemoteAddr *net.UDPAddr) *Link {
+	return &Link{
+		Id:            id,
+		ConnType:      connType,
+		Host:          host,
+		En:            en,
+		De:            de,
+		Crypt:         crypt,
+		Conn:          conn,
+		Flow:          flow,
+		UdpListener:   udpListener,
+		Rate:          rate,
+		UdpRemoteAddr: UdpRemoteAddr,
+	}
+}
+
+type Flow struct {
+	ExportFlow int64 //出口流量
+	InletFlow  int64 //入口流量
+	FlowLimit  int64 //流量限制,出口+入口 /M
+	sync.RWMutex
+}
+
+func (s *Flow) Add(in, out int) {
+	s.Lock()
+	defer s.Unlock()
+	s.InletFlow += int64(in)
+	s.ExportFlow += int64(out)
+}
+
+type Client struct {
+	Cnf       *Config
+	Id        int    //id
+	VerifyKey string //验证密钥
+	Addr      string //客户端ip地址
+	Remark    string //备注
+	Status    bool   //是否开启
+	IsConnect bool   //是否连接
+	RateLimit int    //速度限制 /kb
+	Flow      *Flow  //流量
+	Rate      *Rate  //速度控制
+	id        int
+	sync.RWMutex
+}
+
+func (s *Client) GetId() int {
+	s.Lock()
+	defer s.Unlock()
+	s.id++
+	return s.id
+}
+
+type Tunnel struct {
+	Id           int     //Id
+	TcpPort      int     //服务端与客户端通信端口
+	Mode         string  //启动方式
+	Target       string  //目标
+	Status       bool    //是否开启
+	Client       *Client //所属客户端id
+	Flow         *Flow
+	Config       *Config
+	UseClientCnf bool   //是否继承客户端配置
+	Remark       string //备注
+}
+
+type Config struct {
+	U              string //socks5验证用户名
+	P              string //socks5验证密码
+	Compress       string //压缩方式
+	Crypt          bool   //是否加密
+	CompressEncode int    //加密方式
+	CompressDecode int    //解密方式
+}
+
+type Host struct {
+	Host         string //启动方式
+	Target       string //目标
+	HeaderChange string //host修改
+	HostChange   string //host修改
+	Flow         *Flow
+	Client       *Client
+	Remark       string //备注
+}
+
+//深拷贝Config
+func DeepCopyConfig(c *Config) *Config {
+	return &Config{
+		U:              c.U,
+		P:              c.P,
+		Compress:       c.Compress,
+		Crypt:          c.Crypt,
+		CompressEncode: c.CompressEncode,
+		CompressDecode: c.CompressDecode,
+	}
+}

+ 13 - 5
utils/pool.go

@@ -1,13 +1,15 @@
 package utils
 
-import "sync"
+import (
+	"sync"
+)
 
 const poolSize = 64 * 1024
 const poolSizeSmall = 100
 const poolSizeUdp = 1472
 const poolSizeCopy = 32 * 1024
 
-var bufPool = sync.Pool{
+var BufPool = sync.Pool{
 	New: func() interface{} {
 		return make([]byte, poolSize)
 	},
@@ -18,18 +20,24 @@ var BufPoolUdp = sync.Pool{
 		return make([]byte, poolSizeUdp)
 	},
 }
-var bufPoolMax = sync.Pool{
+var BufPoolMax = sync.Pool{
 	New: func() interface{} {
 		return make([]byte, poolSize)
 	},
 }
-var bufPoolSmall = sync.Pool{
+var BufPoolSmall = sync.Pool{
 	New: func() interface{} {
 		return make([]byte, poolSizeSmall)
 	},
 }
-var bufPoolCopy = sync.Pool{
+var BufPoolCopy = sync.Pool{
 	New: func() interface{} {
 		return make([]byte, poolSizeCopy)
 	},
 }
+
+func PutBufPoolCopy(buf []byte) {
+	if cap(buf) == poolSizeCopy {
+		BufPoolCopy.Put(buf[:poolSizeCopy])
+	}
+}

+ 3 - 93
utils/util.go

@@ -2,7 +2,6 @@ package utils
 
 import (
 	"encoding/base64"
-	"io"
 	"io/ioutil"
 	"log"
 	"net"
@@ -11,8 +10,6 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
-	"sync"
-	"time"
 )
 
 const (
@@ -26,9 +23,8 @@ const (
 	RES_SIGN          = "sign"
 	RES_MSG           = "msg0"
 	RES_CLOSE         = "clse"
+	NEW_CONN          = "conn" //新连接标志
 	CONN_SUCCESS      = "sucs"
-	CONN_ERROR        = "fail"
-	TEST_FLAG         = "tst"
 	CONN_TCP          = "tcp"
 	CONN_UDP          = "udp"
 	UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
@@ -42,32 +38,6 @@ WWW-Authenticate: Basic realm="easyProxy"
 `
 )
 
-//copy
-func Relay(in, out net.Conn, compressType int, crypt, mux bool, rate *Rate) (n int64, err error) {
-	switch compressType {
-	case COMPRESS_SNAPY_ENCODE:
-		n, err = copyBuffer(NewSnappyConn(in, crypt, rate), out)
-		out.Close()
-		NewSnappyConn(in, crypt, rate).Write([]byte(IO_EOF))
-	case COMPRESS_SNAPY_DECODE:
-		n, err = copyBuffer(in, NewSnappyConn(out, crypt, rate))
-		in.Close()
-		if !mux {
-			out.Close()
-		}
-	case COMPRESS_NONE_ENCODE:
-		n, err = copyBuffer(NewCryptConn(in, crypt, rate), out)
-		out.Close()
-		NewCryptConn(in, crypt, rate).Write([]byte(IO_EOF))
-	case COMPRESS_NONE_DECODE:
-		n, err = copyBuffer(in, NewCryptConn(out, crypt, rate))
-		in.Close()
-		if !mux {
-			out.Close()
-		}
-	}
-	return
-}
 
 //判断压缩方式
 func GetCompressType(compress string) (int, int) {
@@ -152,71 +122,11 @@ func GetIntNoErrByStr(str string) int {
 	return i
 }
 
-// io.copy的优化版,读取buffer长度原为32*1024,与snappy不同,导致读取出的内容存在差异,不利于解密
-//内存优化 用到pool,快速回收
-func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
-	for {
-		//放在里面是为了加快回收和重利用
-		buf := bufPoolCopy.Get().([]byte)
-		nr, er := src.Read(buf)
-		if nr > 0 {
-			nw, ew := dst.Write(buf[0:nr])
-			bufPoolCopy.Put(buf)
-			if nw > 0 {
-				written += int64(nw)
-			}
-			if ew != nil {
-				err = ew
-				break
-			}
-			if nr != nw {
-				err = io.ErrShortWrite
-				break
-			}
-		} else {
-			bufPoolCopy.Put(buf)
-		}
-		if er != nil {
-			if er != io.EOF {
-				err = er
-			}
-			break
-		}
-	}
-	return written, err
-}
-
-//连接重置 清空缓存区
-func FlushConn(c net.Conn) {
-	c.SetReadDeadline(time.Now().Add(time.Second * 3))
-	buf := bufPool.Get().([]byte)
-	defer bufPool.Put(buf)
-	for {
-		if _, err := c.Read(buf); err != nil {
-			break
-		}
-	}
-	c.SetReadDeadline(time.Time{})
-}
-
 //简单的一个校验值
 func Getverifyval(vkey string) string {
 	return Md5(vkey)
 }
 
-//wait replay group
-//conn1 网桥 conn2
-func ReplayWaitGroup(conn1 net.Conn, conn2 net.Conn, compressEncode, compressDecode int, crypt, mux bool, rate *Rate) (out int64, in int64) {
-	var wg sync.WaitGroup
-	wg.Add(1)
-	go func() {
-		in, _ = Relay(conn1, conn2, compressEncode, crypt, mux, rate)
-		wg.Done()
-	}()
-	out, _ = Relay(conn2, conn1, compressDecode, crypt, mux, rate)
-	wg.Wait()
-	return
-}
 
 func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) {
 	if host != "" {
@@ -236,8 +146,8 @@ func ChangeHostAndHeader(r *http.Request, host string, header string, addr strin
 	r.Header.Set("X-Real-IP", addr)
 }
 
-func ReadAllFromFile(filePth string) ([]byte, error) {
-	f, err := os.Open(filePth)
+func ReadAllFromFile(filePath string) ([]byte, error) {
+	f, err := os.Open(filePath)
 	if err != nil {
 		return nil, err
 	}

+ 2 - 1
web/controllers/base.go

@@ -40,7 +40,8 @@ func (s *BaseController) display(tpl ...string) {
 	}
 	ip := s.Ctx.Request.Host
 	if strings.LastIndex(ip, ":") > 0 {
-		s.Data["ip"] = utils.GetHostByName(ip[0:])
+		arr := strings.Split(utils.GetHostByName(ip), ":")
+		s.Data["ip"] = arr[0]
 	}
 	s.Data["p"] = server.Bridge.TunnelPort
 	s.Data["proxyPort"] = beego.AppConfig.String("hostPort")

+ 0 - 2
web/controllers/client.go

@@ -38,7 +38,6 @@ func (s *ClientController) Add() {
 				P:        s.GetString("p"),
 				Compress: s.GetString("compress"),
 				Crypt:    s.GetBoolNoErr("crypt"),
-				Mux:      s.GetBoolNoErr("mux"),
 			},
 			RateLimit: s.GetIntNoErr("rate_limit"),
 			Flow: &utils.Flow{
@@ -91,7 +90,6 @@ func (s *ClientController) Edit() {
 			c.Cnf.P = s.GetString("p")
 			c.Cnf.Compress = s.GetString("compress")
 			c.Cnf.Crypt = s.GetBoolNoErr("crypt")
-			c.Cnf.Mux = s.GetBoolNoErr("mux")
 			c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit"))
 			c.RateLimit = s.GetIntNoErr("rate_limit")
 			if c.Rate != nil {

+ 0 - 2
web/controllers/index.go

@@ -81,7 +81,6 @@ func (s *IndexController) Add() {
 				P:        s.GetString("p"),
 				Compress: s.GetString("compress"),
 				Crypt:    s.GetBoolNoErr("crypt"),
-				Mux:      s.GetBoolNoErr("mux"),
 			},
 			Id:           server.CsvDb.GetTaskId(),
 			UseClientCnf: s.GetBoolNoErr("use_client"),
@@ -136,7 +135,6 @@ func (s *IndexController) Edit() {
 			t.Config.P = s.GetString("p")
 			t.Config.Compress = s.GetString("compress")
 			t.Config.Crypt = s.GetBoolNoErr("crypt")
-			t.Config.Mux = s.GetBoolNoErr("mux")
 			t.UseClientCnf = s.GetBoolNoErr("use_client")
 			t.Remark = s.GetString("remark")
 			if t.Client, err = server.CsvDb.GetClient(s.GetIntNoErr("client_id")); err != nil {

+ 0 - 7
web/views/client/add.html

@@ -38,13 +38,6 @@
                             <option value="1">加密</option>
                         </select>
                     </div>
-                    <div class="form-group" id="compress">
-                        <label class="control-label">是否TCP复用</label>
-                        <select class="form-control" name="crypt">
-                            <option value="0">不启用</option>
-                            <option value="1">启用</option>
-                        </select>
-                    </div>
                 </form>
             </div>
             <div class="tile-footer">

+ 0 - 7
web/views/client/edit.html

@@ -43,13 +43,6 @@
                             <option {{if eq  true .c.Cnf.Crypt}}selected{{end}} value="1">加密</option>
                         </select>
                     </div>
-                    <div class="form-group" id="compress">
-                        <label class="control-label">是否启用TCP复用(所有模式均支持)</label>
-                        <select class="form-control" name="mux">
-                            <option {{if eq false .c.Cnf.Mux}}selected{{end}} value="0">不启用</option>
-                            <option {{if eq  true .c.Cnf.Mux}}selected{{end}} value="1">启用</option>
-                        </select>
-                    </div>
                 </form>
             </div>
             <div class="tile-footer">

+ 2 - 14
web/views/client/list.html

@@ -11,7 +11,6 @@
                         <th>备注</th>
                         <th>压缩方式</th>
                         <th>加密</th>
-                        <th>多路复用</th>
                         <th>用户名</th>
                         <th>密码</th>
                         <th>状态</th>
@@ -126,7 +125,6 @@
                         {data: "Addr"},
                         {data: "Addr"},
                         {data: "Addr"},
-                        {data: "Addr"},
                         {data: "Remark"},
                         {data: "Status"},
                         {data: "IsConnect"},
@@ -193,20 +191,10 @@
                             render: function (data, type, row, meta) {
                                 return row.Cnf.U
                             }
-                        },
-                        {
-                            targets: -8,
-                            render: function (data, type, row, meta) {
-                                if (row.Cnf.Mux == "0") {
-                                    return "不启用"
-                                } else {
-                                    return "启用"
-                                }
-                            }
                         }
                         ,
                         {
-                            targets: -9,
+                            targets: -8,
                             render: function (data, type, row, meta) {
                                 if (row.Cnf.Crypt == "0") {
                                     return "不加密"
@@ -217,7 +205,7 @@
                         }
                         ,
                         {
-                            targets: -10,
+                            targets: -9,
                             render: function (data, type, row, meta) {
                                 return row.Cnf.Compress
                             }

+ 0 - 7
web/views/index/add.html

@@ -59,13 +59,6 @@
                             <option value="1">加密</option>
                         </select>
                     </div>
-                    <div class="form-group" id="mux">
-                        <label class="control-label">是否TCP复用</label>
-                        <select class="form-control" name="mux">
-                            <option value="0">不启用</option>
-                            <option value="1">启用</option>
-                        </select>
-                    </div>
                     <div class="form-group" id="u">
                         <label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
                         <input class="form-control" type="text" name="u" placeholder="不填则无需验证">

+ 0 - 7
web/views/index/edit.html

@@ -55,13 +55,6 @@
                             <option {{if eq  true .t.Config.Crypt}}selected{{end}} value="1">加密</option>
                         </select>
                     </div>
-                    <div class="form-group" id="mux">
-                        <label class="control-label">是否启用TCP复用(所有模式均支持)</label>
-                        <select class="form-control" name="mux">
-                            <option {{if eq false .t.Config.Mux}}selected{{end}} value="0">不启用</option>
-                            <option {{if eq  true .t.Config.Mux}}selected{{end}} value="1">启用</option>
-                        </select>
-                    </div>
                     <div class="form-group" id="u">
                         <label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
                         <input class="form-control" value="{{.t.Config.U}}" type="text" name="u"

+ 39 - 30
web/views/index/help.html

@@ -26,26 +26,16 @@
             </p>
             <p><b>使用步骤:</b></p>
             <ul>
-                <li>将a.proxy.com,b.proxy.com解析到公网服务器{{.ip}}</li>
-                <li>使用nginx监听这两个个域名,并配置ssl等……</li>
-                <li>在nginx配置中添加反向代理(或直接将http监听端口改为80)<br>
-                    <pre><code>
-                    server {
-                        listen 80;
-                        server_name a.proxy.com b.proxy.com;#也可以是泛解析*.proxy.com
-                        #ssl等配置
-                        <b>location / {
-                            proxy_pass http://127.0.0.1:{{.proxyPort}};
-                        }</b>
-                    }
-                        </code></pre>
-                </li>
-                <li>在域名代理管理中添加一个客户端,选择压缩方式,保存。 <a href="/index/add?type=hostServer">立即添加</a></li>
-            {{/*<li>在域名代理管理中找到新加的客户端(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
+                <li>将*.proxy.com解析到公网服务器{{.ip}}</li>
+                <li>在客户端管理中创建一个客户端,记录下验证密钥</li>
                 <li>点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82</li>
+                <li>内网客户端运行<code>
+                    <pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
+                </code></pre></li>
                 <li>现在访问a.proxy.com,b.proxy.com即可成功</li>
             </ul>
-            <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
+            <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,<b>如需使用https请在配置文件中将https端口设置为443,和将对应的证书文件路径添加到配置文件中
+            </b></p>
         </div>
     </div>
 </div>
@@ -61,10 +51,13 @@
             </p>
             <p><b>使用步骤:</b></p>
             <ul>
-                <li>在tcp隧道管理中添加一条隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。 <a
-                        href="/index/add?type=tunnelServer">立即添加</a></li>
-            {{/*<li>在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
-                <li>访问公网服务器ip({{.ip}}):填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}</li>
+                <li>在客户端管理中创建一个客户端,记录下验证密钥</li>
+                <li>内网客户端运行<code>
+                    <pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
+                </code></pre>
+                </li>
+                <li>在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。</li>
+                <li>访问公网服务器ip({{.ip}}),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}</li>
             </ul>
             <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
         </div>
@@ -80,9 +73,12 @@
             </p>
             <p><b>使用步骤:</b></p>
             <ul>
-                <li>在udp隧道管理中添加一条隧道,填写监听的端口(8002)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。 <a
-                        href="/index/add?type=udpServer">立即添加</a></li>
-            {{/*<li>在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
+                <li>在客户端管理中创建一个客户端,记录下验证密钥</li>
+                <li>内网客户端运行<code>
+                    <pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
+                </code></pre>
+                </li>
+                <li>在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。</li>
                 <li>修改本机dns为{{.ip}},则相当于使用10.1.50.202作为dns服务器</li>
             </ul>
             <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
@@ -101,9 +97,12 @@
             </p>
             <p><b>使用步骤:</b></p>
             <ul>
-                <li>在socks5隧道管理中添加一条隧道,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。 <a
-                        href="/index/add?type=sock5Server">立即添加</a></li>
-            {{/*<li>在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
+                <li>在客户端管理中创建一个客户端,记录下验证密钥</li>
+                <li>内网客户端运行<code>
+                    <pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
+                </code></pre>
+                </li>
+                <li>在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。</li>
                 <li>在外网环境的本机配置socks5代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8003),即可畅享内网了</li>
             </ul>
             <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
@@ -120,13 +119,23 @@
             </p>
             <p><b>使用步骤:</b></p>
             <ul>
-                <li>在http隧道管理中添加一条隧道,填写监听的端口(8004),选择压缩方式,保存。 <a
-                        href="/index/add?type=httpProxyServer">立即添加</a></li>
-                <li>在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>
+                <li>在客户端管理中创建一个客户端,记录下验证密钥</li>
+                <li>内网客户端运行<code>
+                    <pre>./proxy_client server={{.ip}}:{{.p}} -vkey=客户端的密钥</pre>
+                </code></pre>
+                </li>
+                <li>在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。</li>
                 <li>在外网环境的本机配置http代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8004),即可访问了</li>
             </ul>
             <p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动</p>
         </div>
     </div>
+    <div class="col-md-12">
+        <div class="tile">
+            <p><b>单个客户端可以田间多条隧道或者域名解析</b></p>
+        </div>
+    </div>
 </div>
+
+
 </main>

+ 2 - 14
web/views/index/hlist.html

@@ -12,7 +12,6 @@
                         <th>host改写</th>
                         <th>压缩方式</th>
                         <th>加密</th>
-                        <th>多路复用</th>
                         <th>用户名</th>
                         <th>密码</th>
                         <th>出口流量</th>
@@ -84,7 +83,6 @@
                 {data: 'HostChange'},
                 {data: 'HostChange'},
                 {data: 'HostChange'},
-                {data: 'HostChange'},
                 {data: 'Target'},
             ],
             bFilter: false,
@@ -127,20 +125,10 @@
                     render: function (data, type, row, meta) {
                         return row.Client.Cnf.U
                     }
-                },
-                {
-                    targets: -6,
-                    render: function (data, type, row, meta) {
-                        if (row.Client.Cnf.Mux == "0") {
-                            return "不启用"
-                        } else {
-                            return "启用"
-                        }
-                    }
                 }
                 ,
                 {
-                    targets: -7,
+                    targets: -6,
                     render: function (data, type, row, meta) {
                         if (row.Client.Cnf.Crypt == "0") {
                             return "不加密"
@@ -151,7 +139,7 @@
                 }
                 ,
                 {
-                    targets: -8,
+                    targets: -7,
                     render: function (data, type, row, meta) {
                         return row.Client.Cnf.Compress
                     }

+ 3 - 20
web/views/index/list.html

@@ -12,7 +12,6 @@
                         <th>内网目标</th>
                         <th>压缩方式</th>
                         <th>加密传输</th>
-                        <th>TCP多路复用</th>
                         <th>用户名</th>
                         <th>密码</th>
                         <th>客户端状态</th>
@@ -114,7 +113,6 @@
                 {data: 'Target'},
                 {data: 'Compress'},
                 {data: 'Crypt'},
-                {data: 'Mux'},
                 {data: 'U'},
                 {data: 'P'},
                 {data: 'ClientStatus'},
@@ -152,7 +150,7 @@
                     }
                 }
             }, {
-                targets: 8,
+                targets: 7,
                 render: function (data, type, row, meta) {
                     if (row.UseClientCnf == true) {
                         return row.Client.Cnf.U
@@ -161,7 +159,7 @@
                     }
                 }
             }, {
-                targets: 9,
+                targets: 8,
                 render: function (data, type, row, meta) {
                     if (row.UseClientCnf == true) {
                         return row.Client.Cnf.P
@@ -181,7 +179,7 @@
                     }
                 },
                 {
-                    targets: -9,
+                    targets: -8,
                     render: function (data, type, row, meta) {
                         if (row.UseClientCnf == true) {
                             crypt = row.Client.Cnf.Crypt
@@ -195,21 +193,6 @@
                         }
                     }
                 },
-                {
-                    targets: -8,
-                    render: function (data, type, row, meta) {
-                        if (row.UseClientCnf == true) {
-                            mux = row.Client.Cnf.Mux
-                        } else {
-                            mux = row.Config.Mux
-                        }
-                        if (mux == "0") {
-                            return "不启用"
-                        } else {
-                            return "启用"
-                        }
-                    }
-                },
                 {
                     targets: -5,
                     render: function (data, type, row, meta) {