diff --git a/src/go.mod b/src/go.mod
index 9d154fb..6377e99 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -10,4 +10,7 @@ require (
gopkg.in/yaml.v3 v3.0.1
)
-require golang.org/x/sys v0.37.0
+require (
+ github.com/google/uuid v1.6.0
+ golang.org/x/sys v0.37.0
+)
diff --git a/src/go.sum b/src/go.sum
index a690b8e..f5485b2 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -1,3 +1,5 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
diff --git a/src/server/api/api.go b/src/server/api/api.go
index ff767cf..d14cee0 100644
--- a/src/server/api/api.go
+++ b/src/server/api/api.go
@@ -61,13 +61,19 @@ type Response struct {
// RegisterRoutes 注册路由
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
+ // API 路由
mux.HandleFunc("/api/mapping/create", h.authMiddleware(h.handleCreateMapping))
mux.HandleFunc("/api/mapping/remove", h.authMiddleware(h.handleRemoveMapping))
mux.HandleFunc("/api/mapping/list", h.authMiddleware(h.handleListMappings))
mux.HandleFunc("/api/stats/traffic", h.authMiddleware(h.handleGetTrafficStats))
mux.HandleFunc("/api/stats/history", h.authMiddleware(h.handleGetTrafficHistory))
mux.HandleFunc("/api/stats/monitor", h.authMiddleware(h.handleTrafficMonitor))
- mux.HandleFunc("/admin", h.handleManagement)
+ mux.HandleFunc("/api/stats/connections", h.authMiddleware(h.handleGetActiveConnections))
+
+ // 页面路由
+ mux.HandleFunc("/", h.handleRoot)
+ mux.HandleFunc("/login", h.handleLogin)
+ mux.HandleFunc("/dashboard", h.handleDashboard)
mux.HandleFunc("/health", h.handleHealth)
}
@@ -384,10 +390,31 @@ func (h *Handler) handleTrafficMonitor(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, GetTraffticMonitorHTML())
}
-// handleManagement 管理页面
-func (h *Handler) handleManagement(w http.ResponseWriter, r *http.Request) {
+// handleConnections 连接监控页面
+func (h *Handler) handleConnections(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- fmt.Fprint(w, GetManagementHTML())
+ fmt.Fprint(w, GetConnectionsHTML())
+}
+
+// handleRoot 根路径重定向
+func (h *Handler) handleRoot(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ http.NotFound(w, r)
+ return
+ }
+ http.Redirect(w, r, "/login", http.StatusFound)
+}
+
+// handleLogin 登录页面
+func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ fmt.Fprint(w, GetLoginHTML())
+}
+
+// handleDashboard 仪表板页面
+func (h *Handler) handleDashboard(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ fmt.Fprint(w, GetDashboardHTML())
}
// handleGetTrafficHistory 获取历史流量统计
@@ -436,3 +463,27 @@ func (h *Handler) handleGetTrafficHistory(w http.ResponseWriter, r *http.Request
"count": len(records),
})
}
+
+// handleGetActiveConnections 获取所有活跃连接信息
+func (h *Handler) handleGetActiveConnections(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ h.writeError(w, http.StatusMethodNotAllowed, "只支持 GET 方法")
+ return
+ }
+
+ // 获取所有活跃连接
+ connectionsStats := h.forwarderMgr.GetAllActiveConnections()
+
+ // 按端口号排序
+ sort.Slice(connectionsStats, func(i, j int) bool {
+ return connectionsStats[i].SourcePort < connectionsStats[j].SourcePort
+ })
+
+ // 构建响应
+ response := stats.AllConnectionsStats{
+ Mappings: connectionsStats,
+ Timestamp: time.Now().Unix(),
+ }
+
+ h.writeSuccess(w, "获取活跃连接成功", response)
+}
diff --git a/src/server/api/html.go b/src/server/api/html.go
index 0a945ed..7a78f4c 100644
--- a/src/server/api/html.go
+++ b/src/server/api/html.go
@@ -29,3 +29,45 @@ func GetManagementHTML() string {
}
return string(buf[:n])
}
+
+func GetConnectionsHTML() string {
+ file, err := html.AssetsFS.Open("connections.html")
+ if err != nil {
+ return "
Failed to load HTML
"
+ }
+ defer file.Close()
+ buf := make([]byte, 40960) // 40KB buffer
+ n, err := file.Read(buf)
+ if err != nil {
+ return "Failed to read HTML
"
+ }
+ return string(buf[:n])
+}
+
+func GetLoginHTML() string {
+ file, err := html.AssetsFS.Open("login.html")
+ if err != nil {
+ return "Failed to load HTML
"
+ }
+ defer file.Close()
+ buf := make([]byte, 40960) // 40KB buffer
+ n, err := file.Read(buf)
+ if err != nil {
+ return "Failed to read HTML
"
+ }
+ return string(buf[:n])
+}
+
+func GetDashboardHTML() string {
+ file, err := html.AssetsFS.Open("dashboard.html")
+ if err != nil {
+ return "Failed to load HTML
"
+ }
+ defer file.Close()
+ buf := make([]byte, 81920) // 80KB buffer for dashboard
+ n, err := file.Read(buf)
+ if err != nil {
+ return "Failed to read HTML
"
+ }
+ return string(buf[:n])
+}
diff --git a/src/server/forwarder/forwarder.go b/src/server/forwarder/forwarder.go
index ed0f13b..ecc2ddd 100644
--- a/src/server/forwarder/forwarder.go
+++ b/src/server/forwarder/forwarder.go
@@ -12,6 +12,8 @@ import (
"time"
"golang.org/x/time/rate"
+
+ "github.com/google/uuid"
)
// TunnelServer 隧道服务器接口
@@ -39,6 +41,19 @@ type Forwarder struct {
bytesReceived uint64 // 接收字节数
limiterOut *rate.Limiter // 限速器(出方向)
limiterIn *rate.Limiter // 限速器(入方向)
+
+ // 活跃连接管理
+ connections map[string]*activeConnection // 活跃连接映射
+ connMutex sync.RWMutex // 连接映射锁
+}
+
+// activeConnection 活跃连接信息
+type activeConnection struct {
+ clientAddr string
+ targetAddr string
+ bytesSent uint64
+ bytesReceived uint64
+ connectedAt int64
}
// NewForwarder 创建新的端口转发器
@@ -57,15 +72,16 @@ func NewForwarder(sourcePort int, targetHost string, targetPort int, limit *int6
limiterIn = rate.NewLimiter(rate.Limit(*limit), burst)
}
return &Forwarder{
- sourcePort: sourcePort,
- targetPort: targetPort,
- targetHost: targetHost,
- cancel: cancel,
- ctx: ctx,
- useTunnel: false,
- limit: limit,
- limiterOut: limiterOut,
- limiterIn: limiterIn,
+ sourcePort: sourcePort,
+ targetPort: targetPort,
+ targetHost: targetHost,
+ cancel: cancel,
+ ctx: ctx,
+ useTunnel: false,
+ limit: limit,
+ limiterOut: limiterOut,
+ limiterIn: limiterIn,
+ connections: make(map[string]*activeConnection),
}
}
@@ -95,6 +111,7 @@ func NewTunnelForwarder(sourcePort int, targetHost string, targetPort int, tunne
limit: limit,
limiterOut: limiterOut,
limiterIn: limiterIn,
+ connections: make(map[string]*activeConnection),
}
}
@@ -189,15 +206,19 @@ func (rlr *rateLimitedReader) Read(p []byte) (int, error) {
// countingWriter 带统计的 Writer
type countingWriter struct {
- w io.Writer
- counter *uint64
- port int
+ w io.Writer
+ counter *uint64
+ port int
+ connCounter *uint64 // 连接级别的计数器
}
func (cw *countingWriter) Write(p []byte) (int, error) {
n, err := cw.w.Write(p)
if n > 0 {
atomic.AddUint64(cw.counter, uint64(n))
+ if cw.connCounter != nil {
+ atomic.AddUint64(cw.connCounter, uint64(n))
+ }
}
return n, err
}
@@ -207,7 +228,11 @@ func (f *Forwarder) handleConnection(clientConn net.Conn) {
defer f.wg.Done()
defer clientConn.Close()
- log.Printf("端口 %d 收到新连接: %s", f.sourcePort, clientConn.RemoteAddr())
+ // 生成连接ID
+ connID := uuid.New().String()
+ clientAddr := clientConn.RemoteAddr().String()
+
+ log.Printf("端口 %d 收到新连接: %s (连接ID: %s)", f.sourcePort, clientAddr, connID)
var targetConn net.Conn
var err error
@@ -243,6 +268,30 @@ func (f *Forwarder) handleConnection(clientConn net.Conn) {
defer targetConn.Close()
+ // 记录活跃连接
+ targetAddr := fmt.Sprintf("%s:%d", f.targetHost, f.targetPort)
+ conn := &activeConnection{
+ clientAddr: clientAddr,
+ targetAddr: targetAddr,
+ bytesSent: 0,
+ bytesReceived: 0,
+ connectedAt: time.Now().Unix(),
+ }
+ f.connMutex.Lock()
+ f.connections[connID] = conn
+ f.connMutex.Unlock()
+
+ // 连接关闭时移除记录
+ defer func() {
+ f.connMutex.Lock()
+ delete(f.connections, connID)
+ f.connMutex.Unlock()
+ log.Printf("端口 %d 连接关闭: %s (连接ID: %s, 发送: %d, 接收: %d)",
+ f.sourcePort, clientAddr, connID,
+ atomic.LoadUint64(&conn.bytesSent),
+ atomic.LoadUint64(&conn.bytesReceived))
+ }()
+
// 双向转发
var wg sync.WaitGroup
wg.Add(2)
@@ -256,9 +305,10 @@ func (f *Forwarder) handleConnection(clientConn net.Conn) {
ctx: f.ctx,
}
writer := &countingWriter{
- w: targetConn,
- counter: &f.bytesSent,
- port: f.sourcePort,
+ w: targetConn,
+ counter: &f.bytesSent,
+ port: f.sourcePort,
+ connCounter: &conn.bytesSent,
}
n, _ := io.Copy(writer, reader)
log.Printf("端口 %d: 客户端->目标传输完成,本次发送 %d 字节 (总发送: %d)", f.sourcePort, n, atomic.LoadUint64(&f.bytesSent))
@@ -277,9 +327,10 @@ func (f *Forwarder) handleConnection(clientConn net.Conn) {
ctx: f.ctx,
}
writer := &countingWriter{
- w: clientConn,
- counter: &f.bytesReceived,
- port: f.sourcePort,
+ w: clientConn,
+ counter: &f.bytesReceived,
+ port: f.sourcePort,
+ connCounter: &conn.bytesReceived,
}
n, _ := io.Copy(writer, reader)
log.Printf("端口 %d: 目标->客户端传输完成,本次接收 %d 字节 (总接收: %d)", f.sourcePort, n, atomic.LoadUint64(&f.bytesReceived))
@@ -442,3 +493,43 @@ func (m *Manager) GetAllTrafficStats() map[int]stats.TrafficStats {
return statsMap
}
+
+// GetActiveConnections 获取某个转发器的活跃连接信息
+func (f *Forwarder) GetActiveConnections() []stats.ConnectionInfo {
+ f.connMutex.RLock()
+ defer f.connMutex.RUnlock()
+
+ connections := make([]stats.ConnectionInfo, 0, len(f.connections))
+ for _, conn := range f.connections {
+ connections = append(connections, stats.ConnectionInfo{
+ ClientAddr: conn.clientAddr,
+ TargetAddr: conn.targetAddr,
+ BytesSent: atomic.LoadUint64(&conn.bytesSent),
+ BytesReceived: atomic.LoadUint64(&conn.bytesReceived),
+ ConnectedAt: conn.connectedAt,
+ })
+ }
+
+ return connections
+}
+
+// GetAllActiveConnections 获取所有转发器的活跃连接信息
+func (m *Manager) GetAllActiveConnections() []stats.PortConnectionStats {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+
+ allStats := make([]stats.PortConnectionStats, 0, len(m.forwarders))
+ for port, forwarder := range m.forwarders {
+ connections := forwarder.GetActiveConnections()
+ allStats = append(allStats, stats.PortConnectionStats{
+ SourcePort: port,
+ TargetHost: forwarder.targetHost,
+ TargetPort: forwarder.targetPort,
+ UseTunnel: forwarder.useTunnel,
+ ActiveConnections: connections,
+ TotalConnections: len(connections),
+ })
+ }
+
+ return allStats
+}
diff --git a/src/server/html/dashboard.html b/src/server/html/dashboard.html
new file mode 100644
index 0000000..04923f5
--- /dev/null
+++ b/src/server/html/dashboard.html
@@ -0,0 +1,1337 @@
+
+
+
+
+
+ Go Tunnel 管理系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ currentPageTitle }}
+
+
+
+
+ {{ systemStatus ? '● 系统正常' : '● 离线' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ stats.mappings }}
+
活跃中
+
+
+
+
+
{{ stats.connections }}
+
实时监控
+
+
+
+
+
{{ formatBytes(stats.totalSent) }}
+
+
+
+
+
{{ formatBytes(stats.totalReceived) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ notification.message }}
+
+
+
+
+
+
diff --git a/src/server/html/login.html b/src/server/html/login.html
new file mode 100644
index 0000000..c17334e
--- /dev/null
+++ b/src/server/html/login.html
@@ -0,0 +1,428 @@
+
+
+
+
+
+ 登录 - Go Tunnel 管理系统
+
+
+
+
+
+
🚀 Go Tunnel
+
强大的端口转发与隧道管理系统
+
+ - 实时流量监控
+ - 活跃连接管理
+ - 支持隧道模式
+ - 带宽限制控制
+ - 可视化管理界面
+
+
+
+
+
+
+
+
+
diff --git a/src/server/html/management.html b/src/server/html/management.html
deleted file mode 100644
index 282f16d..0000000
--- a/src/server/html/management.html
+++ /dev/null
@@ -1,844 +0,0 @@
-
-
-
-
-
-
- 端口映射管理 - Go Tunnel
-
-
-
-
-
-
🚀 端口映射管理中心
-
-
-
-
-
-
-
-
-
-
-
-
系统概览
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
管理端口映射
-
-
-
-
-
-
-
-
-
-
-
-
-
流量监控
-
-
-
- 点击下方按钮打开详细的流量监控页面
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/server/html/traffic_monitor.html b/src/server/html/traffic_monitor.html
index ad85357..e7a42f7 100644
--- a/src/server/html/traffic_monitor.html
+++ b/src/server/html/traffic_monitor.html
@@ -3,7 +3,6 @@
- 流量监控 - Go Tunnel