清除已忽略文件并同步远程

This commit is contained in:
qaq
2025-06-17 12:32:17 +08:00
parent 1c23753175
commit d64a1a343d
5 changed files with 0 additions and 654 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"java.compile.nullAnalysis.mode": "automatic"
}
-4
View File
@@ -1,4 +0,0 @@
{
"addr": "127.0.0.1:6365",
"secret": "840b7b728d4c4a51a337966d6e5e78fb"
}
-21
View File
@@ -1,21 +0,0 @@
{
"services": [
{
"name": "web_api",
"addr": ":18080",
"handler": {
"type": "api",
"auth": {
"username": "840b7b728d4c4a51a337966d6e5e78fb",
"password": "840b7b728d4c4a51a337966d6e5e78fb"
},
"metadata": {
"pathprefix": "/api"
}
},
"listener": {
"type": "tcp"
}
}
]
}
-176
View File
@@ -1,176 +0,0 @@
package traffic
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
)
// 全局变量存储HTTP地址
var httpReportURL string
// TrafficReportItem 流量报告项(压缩格式)
type TrafficReportItem struct {
N string `json:"n"` // 服务名(name缩写)
T string `json:"t"` // 连接类型:conn, cc(type缩写)
U int64 `json:"u"` // 上行流量(up缩写)
D int64 `json:"d"` // 下行流量(down缩写)
}
// SetHTTPReportURL 设置HTTP报告地址
func SetHTTPReportURL(addr string, secret string) {
httpReportURL = "http://" + addr + "/flow/upload?secret=" + secret
}
// StartTrafficReporter 启动流量报告任务
func StartTrafficReporter(trafficMgr *TrafficManager) {
// 检查是否设置了HTTP地址
if httpReportURL == "" {
fmt.Println("❌ HTTP报告地址未设置,无法启动流量报告任务")
return
}
ticker := time.NewTicker(5 * time.Second)
go func() {
defer ticker.Stop()
for range ticker.C {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// 原子性地获取并重置流量统计,避免上报期间产生的数据被误清零
stats, err := trafficMgr.GetAndResetAllServicesStats(ctx)
if err != nil {
fmt.Printf("获取流量统计失败: %v\n", err)
cancel()
continue
}
if len(stats) == 0 {
fmt.Println("无流量数据需要报告")
cancel()
continue
}
// 构建报告数据为数组格式,分别处理不同类型的流量
var reportItems []TrafficReportItem
totalServices := 0
totalTraffic := int64(0)
// 解析服务和类型,分组处理
serviceGroups := make(map[string]map[string]map[string]int64) // service -> type -> direction -> bytes
for serviceKey, serviceStats := range stats {
upload := serviceStats["upload"]
download := serviceStats["download"]
// 解析服务名和类型 (format: service:type)
var serviceName, serviceType string
parts := strings.Split(serviceKey, ":")
if len(parts) >= 2 {
serviceName = parts[0]
serviceType = parts[1]
} else {
serviceName = serviceKey
serviceType = "unknown"
}
if serviceGroups[serviceName] == nil {
serviceGroups[serviceName] = make(map[string]map[string]int64)
}
if serviceGroups[serviceName][serviceType] == nil {
serviceGroups[serviceName][serviceType] = make(map[string]int64)
}
serviceGroups[serviceName][serviceType]["upload"] = upload
serviceGroups[serviceName][serviceType]["download"] = download
totalTraffic += upload + download
}
// 为每个服务的每种类型创建报告项
for serviceName, types := range serviceGroups {
for serviceType, trafficData := range types {
// 过滤掉total类型,只保留conn和cc
if serviceType == "total" {
continue
}
reportItems = append(reportItems, TrafficReportItem{
N: serviceName,
T: serviceType,
U: trafficData["upload"],
D: trafficData["download"],
})
totalServices++
}
}
// 发送到HTTP接口
success, err := sendTrafficReport(ctx, reportItems)
if err != nil {
fmt.Printf("❌ 发送流量报告失败: %v (数据已清零,本次数据丢失)\n", err)
} else if success {
fmt.Printf("✅ 流量报告已发送: %d个记录, 总流量: %d bytes\n",
totalServices, totalTraffic)
} else {
fmt.Printf("⚠️ 服务器未确认(非ok响应): %d个记录, 总流量: %d bytes (数据已清零,本次数据丢失)\n",
totalServices, totalTraffic)
}
cancel()
}
}()
fmt.Printf("🚀 流量报告任务已启动 (5秒间隔),目标地址: %s\n", httpReportURL)
}
// sendTrafficReport 发送流量报告到HTTP接口
func sendTrafficReport(ctx context.Context, reportItems []TrafficReportItem) (bool, error) {
jsonData, err := json.Marshal(reportItems)
if err != nil {
return false, fmt.Errorf("序列化报告数据失败: %v", err)
}
req, err := http.NewRequestWithContext(ctx, "POST", httpReportURL, bytes.NewBuffer(jsonData))
if err != nil {
return false, fmt.Errorf("创建HTTP请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "GOST-Traffic-Reporter/1.0")
client := &http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
return false, fmt.Errorf("发送HTTP请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("HTTP响应错误: %d %s", resp.StatusCode, resp.Status)
}
// 读取响应内容
var responseBytes bytes.Buffer
_, err = responseBytes.ReadFrom(resp.Body)
if err != nil {
return false, fmt.Errorf("读取响应内容失败: %v", err)
}
responseText := strings.TrimSpace(responseBytes.String())
// 检查响应是否为"ok"
if responseText == "ok" {
return true, nil
} else {
return false, fmt.Errorf("服务器响应: %s (期望: ok)", responseText)
}
}
-450
View File
@@ -1,450 +0,0 @@
package traffic
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
psnet "github.com/shirou/gopsutil/v3/net"
)
// SystemInfo 系统信息结构体
type SystemInfo struct {
Uptime uint64 `json:"uptime"` // 开机时间 (秒)
BytesReceived uint64 `json:"bytes_received"` // 接收字节数
BytesTransmitted uint64 `json:"bytes_transmitted"` // 发送字节数
CPUUsage float64 `json:"cpu_usage"` // CPU使用率(百分比)
MemoryUsage float64 `json:"memory_usage"` // 内存使用率(百分比)
}
// NetworkStats 网络统计信息
type NetworkStats struct {
BytesReceived uint64 `json:"bytes_received"` // 接收字节数
BytesTransmitted uint64 `json:"bytes_transmitted"` // 发送字节数
}
// CPUInfo CPU信息
type CPUInfo struct {
Usage float64 `json:"usage"` // CPU使用率(百分比)
}
// MemoryInfo 内存信息
type MemoryInfo struct {
Usage float64 `json:"usage"` // 内存使用率(百分比)
}
type WebSocketReporter struct {
url string
conn *websocket.Conn
reconnectTime time.Duration
pingInterval time.Duration
ctx context.Context
cancel context.CancelFunc
connected bool
}
// NewWebSocketReporter 创建一个新的WebSocket报告器
func NewWebSocketReporter(serverURL string) *WebSocketReporter {
ctx, cancel := context.WithCancel(context.Background())
return &WebSocketReporter{
url: serverURL,
reconnectTime: 5 * time.Second, // 重连间隔
pingInterval: 2 * time.Second, // 发送间隔改为2秒
ctx: ctx,
cancel: cancel,
connected: false,
}
}
// Start 启动WebSocket报告器
func (w *WebSocketReporter) Start() {
go w.run()
}
// Stop 停止WebSocket报告器
func (w *WebSocketReporter) Stop() {
w.cancel()
if w.conn != nil {
w.conn.Close()
}
}
// run 主运行循环
func (w *WebSocketReporter) run() {
for {
select {
case <-w.ctx.Done():
return
default:
if err := w.connect(); err != nil {
fmt.Printf("❌ WebSocket连接失败: %v,%v后重试\n", err, w.reconnectTime)
select {
case <-time.After(w.reconnectTime):
continue
case <-w.ctx.Done():
return
}
}
// 连接成功,开始发送消息
w.handleConnection()
}
}
}
// connect 建立WebSocket连接
func (w *WebSocketReporter) connect() error {
u, err := url.Parse(w.url)
if err != nil {
return fmt.Errorf("解析URL失败: %v", err)
}
dialer := websocket.DefaultDialer
dialer.HandshakeTimeout = 10 * time.Second
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return fmt.Errorf("连接WebSocket失败: %v", err)
}
w.conn = conn
w.connected = true
// 设置关闭处理器来检测连接状态
w.conn.SetCloseHandler(func(code int, text string) error {
w.connected = false
return nil
})
return nil
}
// handleConnection 处理WebSocket连接
func (w *WebSocketReporter) handleConnection() {
defer func() {
if w.conn != nil {
w.conn.Close()
w.conn = nil
}
w.connected = false
}()
// 主发送循环
ticker := time.NewTicker(w.pingInterval)
defer ticker.Stop()
for {
select {
case <-w.ctx.Done():
return
case <-ticker.C:
// 检查连接状态
if !w.connected {
return
}
// 获取系统信息并发送
sysInfo := w.collectSystemInfo()
if err := w.sendSystemInfo(sysInfo); err != nil {
fmt.Printf("❌ 发送系统信息失败: %v,准备重连\n", err)
return
}
}
}
}
// collectSystemInfo 收集系统信息
func (w *WebSocketReporter) collectSystemInfo() SystemInfo {
networkStats := getNetworkStats()
cpuInfo := getCPUInfo()
memoryInfo := getMemoryInfo()
return SystemInfo{
Uptime: getUptime(),
BytesReceived: networkStats.BytesReceived,
BytesTransmitted: networkStats.BytesTransmitted,
CPUUsage: cpuInfo.Usage,
MemoryUsage: memoryInfo.Usage,
}
}
// sendSystemInfo 发送系统信息
func (w *WebSocketReporter) sendSystemInfo(sysInfo SystemInfo) error {
if w.conn == nil || !w.connected {
return fmt.Errorf("连接未建立")
}
// 转换为JSON
jsonData, err := json.Marshal(sysInfo)
if err != nil {
return fmt.Errorf("序列化系统信息失败: %v", err)
}
// 设置写入超时
w.conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err := w.conn.WriteMessage(websocket.TextMessage, jsonData); err != nil {
w.connected = false // 标记连接已断开
return fmt.Errorf("写入消息失败: %v", err)
}
return nil
}
// getHostIP 获取主机IP地址(IPv4优先策略:公网v4 → 公网v6 → 内网v4 → 内网v6)
func getHostIP() string {
// 先尝试获取IPv4地址
ipv4 := getLocalIPv4()
if ipv4 != "unknown" && !isPrivateIP(ipv4) {
return ipv4
}
// 尝试获取IPv6地址
ipv6 := getLocalIPv6()
if ipv6 != "unknown" && !isPrivateIP(ipv6) {
return ipv6
}
// IPv4公网IP查询服务
ipv4Services := []string{
"https://ipv4.icanhazip.com",
"https://api.ipify.org",
"https://checkip.amazonaws.com",
"https://myip.biturl.top",
}
// IPv6公网IP查询服务
ipv6Services := []string{
"https://ipv6.icanhazip.com",
"https://v6.ident.me",
"https://ipv6.myip.biturl.top",
}
client := &http.Client{
Timeout: 10 * time.Second,
}
// 优先尝试获取IPv4公网地址
for _, service := range ipv4Services {
if ip := getIPFromService(client, service); ip != "" && net.ParseIP(ip).To4() != nil {
return strings.TrimSpace(ip)
}
}
// IPv4公网IP获取失败,尝试获取IPv6公网地址
if ipv6 != "unknown" {
for _, service := range ipv6Services {
if ip := getIPFromService(client, service); ip != "" && net.ParseIP(ip).To4() == nil {
return strings.TrimSpace(ip)
}
}
}
// 如果所有公网IP服务都失败,按优先级返回本地IP:IPv4 → IPv6
if ipv4 != "unknown" {
return ipv4
}
if ipv6 != "unknown" {
return ipv6
}
return "unknown"
}
// getIPFromService 从指定服务获取IP地址
func getIPFromService(client *http.Client, serviceURL string) string {
resp, err := client.Get(serviceURL)
if err != nil {
return ""
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return ""
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return ""
}
ip := strings.TrimSpace(string(body))
// 简单验证是否为有效的IP地址
if net.ParseIP(ip) != nil {
return ip
}
return ""
}
// getLocalIP 获取本地接口IP地址(作为备用方案,保持向后兼容)
func getLocalIP() string {
return getLocalIPv4()
}
// getLocalIPv4 获取本地IPv4接口地址
func getLocalIPv4() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return "unknown"
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}
// getLocalIPv6 获取本地IPv6接口地址
func getLocalIPv6() string {
// 使用Google的IPv6 DNS服务器
conn, err := net.Dial("udp", "[2001:4860:4860::8888]:80")
if err != nil {
return "unknown"
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}
// isPrivateIP 判断IP地址是否为内网地址
func isPrivateIP(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
// 检查IPv4私有地址范围
if ip.To4() != nil {
// 10.0.0.0/8
if ip[12] == 10 {
return true
}
// 172.16.0.0/12
if ip[12] == 172 && ip[13] >= 16 && ip[13] <= 31 {
return true
}
// 192.168.0.0/16
if ip[12] == 192 && ip[13] == 168 {
return true
}
// 127.0.0.0/8 (回环地址)
if ip[12] == 127 {
return true
}
}
// 检查IPv6私有地址
if ip.To4() == nil {
// ::1 (回环地址)
if ip.IsLoopback() {
return true
}
// fc00::/7 (唯一本地地址 Unique Local Addresses)
if len(ip) >= 2 && (ip[0]&0xfe) == 0xfc {
return true
}
// fe80::/10 (链路本地地址 Link Local)
if len(ip) >= 2 && ip[0] == 0xfe && (ip[1]&0xc0) == 0x80 {
return true
}
// ::ffff:0:0/96 (IPv4映射地址)
if len(ip) >= 12 && ip[10] == 0xff && ip[11] == 0xff {
// 检查映射的IPv4地址是否为私有地址
ipv4 := net.IPv4(ip[12], ip[13], ip[14], ip[15])
return isPrivateIP(ipv4.String())
}
// ::/128 (未指定地址)
if ip.IsUnspecified() {
return true
}
}
return false
}
// getUptime 获取系统开机时间(秒)
func getUptime() uint64 {
uptime, err := host.Uptime()
if err != nil {
return 0
}
return uptime
}
// getNetworkStats 获取网络统计信息
func getNetworkStats() NetworkStats {
var stats NetworkStats
ioCounters, err := psnet.IOCounters(true)
if err != nil {
fmt.Printf("获取网络统计失败: %v\n", err)
return stats
}
// 汇总所有非回环接口的流量
for _, io := range ioCounters {
// 跳过回环接口
if io.Name == "lo" || strings.HasPrefix(io.Name, "lo") {
continue
}
stats.BytesReceived += io.BytesRecv
stats.BytesTransmitted += io.BytesSent
}
return stats
}
// getCPUInfo 获取CPU信息
func getCPUInfo() CPUInfo {
var cpuInfo CPUInfo
// 获取CPU使用率
percentages, err := cpu.Percent(time.Second, false)
if err == nil && len(percentages) > 0 {
cpuInfo.Usage = percentages[0]
}
return cpuInfo
}
// getMemoryInfo 获取内存信息
func getMemoryInfo() MemoryInfo {
var memInfo MemoryInfo
vmStat, err := mem.VirtualMemory()
if err != nil {
return memInfo
}
memInfo.Usage = vmStat.UsedPercent
return memInfo
}
// StartWebSocketReporterWithConfig 使用配置启动WebSocket报告器
func StartWebSocketReporterWithConfig(Addr string, Secret string) *WebSocketReporter {
// 获取本机IP地址
localIP := getHostIP()
// 构建包含本机IP的WebSocket URL
var fullURL = "ws://" + Addr + "/system-info?type=1&secret=" + Secret + "&client_ip=" + localIP
fmt.Printf("🔗 WebSocket连接URL: %s\n", fullURL)
reporter := NewWebSocketReporter(fullURL)
reporter.Start()
return reporter
}