mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 00:06:38 +08:00
修复暂停bug
This commit is contained in:
@@ -547,10 +547,6 @@ func pauseService(ctx *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// 使用和 updateService 相同的方法彻底断开所有连接
|
||||
svc.Close()
|
||||
registry.ServiceRegistry().Unregister(name)
|
||||
|
||||
// 强制断开端口的所有连接
|
||||
if serviceAddr != "" {
|
||||
_ = kill.ForceClosePortConnections(serviceAddr)
|
||||
@@ -912,10 +908,6 @@ func pauseServices(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 使用和 updateService 相同的方法彻底断开所有连接
|
||||
stp.service.Close()
|
||||
registry.ServiceRegistry().Unregister(stp.name)
|
||||
|
||||
// 强制断开端口的所有连接
|
||||
if serviceConfig.Addr != "" {
|
||||
_ = kill.ForceClosePortConnections(serviceConfig.Addr)
|
||||
|
||||
@@ -3,8 +3,9 @@ package port
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -16,178 +17,101 @@ func ForceClosePortConnections(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析地址和端口
|
||||
host, portStr, err := net.SplitHostPort(addr)
|
||||
_, portStr, err := splitHostPort(addr)
|
||||
if err != nil {
|
||||
// 如果解析失败,可能是因为没有端口,或者格式不对
|
||||
return fmt.Errorf("failed to parse address %s: %v", addr, err)
|
||||
return fmt.Errorf("parse address failed: %v", err)
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port %s: %v", portStr, err)
|
||||
return fmt.Errorf("invalid port: %v", err)
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return forceClosePortConnectionsLinux(host, port)
|
||||
default:
|
||||
// 对于非Linux系统,静默忽略
|
||||
if runtime.GOOS != "linux" {
|
||||
fmt.Println("Warning: current OS is not Linux, skipping connection close.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return closePortConnectionsBySS(port)
|
||||
}
|
||||
|
||||
// forceClosePortConnectionsLinux 在Linux上通过/proc/net/tcp*读取连接并关闭
|
||||
func forceClosePortConnectionsLinux(host string, port int) error {
|
||||
// 关闭IPv4连接
|
||||
if err := closeConnectionsFromProcNet("/proc/net/tcp", port); err != nil {
|
||||
fmt.Printf("Warning: failed to close IPv4 connections on port %d: %v\n", port, err)
|
||||
// splitHostPort 是 net.SplitHostPort 的包装,容错增强
|
||||
func splitHostPort(addr string) (string, string, error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
return "", "", fmt.Errorf("missing port in address")
|
||||
}
|
||||
|
||||
// 关闭IPv6连接
|
||||
if err := closeConnectionsFromProcNet("/proc/net/tcp6", port); err != nil {
|
||||
fmt.Printf("Warning: failed to close IPv6 connections on port %d: %v\n", port, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return netSplitHostPortCompat(addr)
|
||||
}
|
||||
|
||||
// closeConnectionsFromProcNet 从/proc/net/tcp*文件读取连接信息并关闭连接
|
||||
func closeConnectionsFromProcNet(procFile string, targetPort int) error {
|
||||
file, err := os.Open(procFile)
|
||||
// netSplitHostPortCompat 是增强版本的 SplitHostPort,兼容无 host 情况
|
||||
func netSplitHostPortCompat(addr string) (string, string, error) {
|
||||
lastColon := strings.LastIndex(addr, ":")
|
||||
if lastColon < 0 {
|
||||
return "", "", fmt.Errorf("missing port")
|
||||
}
|
||||
return addr[:lastColon], addr[lastColon+1:], nil
|
||||
}
|
||||
|
||||
// closePortConnectionsBySS 通过 ss 找到 pid/fd 然后关闭连接
|
||||
func closePortConnectionsBySS(targetPort int) error {
|
||||
cmd := exec.Command("ss", "-tnp")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to execute ss: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
// 跳过第一行(header)
|
||||
scanner.Scan()
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(out)))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 10 {
|
||||
|
||||
if !strings.Contains(line, fmt.Sprintf(":%d", targetPort)) || !strings.Contains(line, "ESTAB") {
|
||||
continue
|
||||
}
|
||||
|
||||
// local_address字段格式: IP:PORT (hex)
|
||||
localAddr := fields[1]
|
||||
parts := strings.Split(localAddr, ":")
|
||||
if len(parts) != 2 {
|
||||
// 例如:users:(("gost",pid=1234,fd=17))
|
||||
start := strings.Index(line, "pid=")
|
||||
if start == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析端口(十六进制)
|
||||
portHex := parts[1]
|
||||
port, err := strconv.ParseInt(portHex, 16, 32)
|
||||
rest := line[start:]
|
||||
parts := strings.Split(rest, ",")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
pidStr := strings.TrimPrefix(parts[0], "pid=")
|
||||
fdStr := strings.TrimPrefix(parts[1], "fd=")
|
||||
pid := strings.Trim(pidStr, ")")
|
||||
fd := strings.Trim(fdStr, ")")
|
||||
|
||||
fdPath := fmt.Sprintf("/proc/%s/fd/%s", pid, fd)
|
||||
realPath, err := filepath.EvalSymlinks(fdPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: unable to resolve %s: %v\n", fdPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否是目标端口
|
||||
if int(port) == targetPort {
|
||||
// 获取远程地址信息
|
||||
remoteAddr := fields[2]
|
||||
state := fields[3]
|
||||
|
||||
// 只关闭已建立的连接,跳过监听状态
|
||||
if state == "0A" { // LISTEN状态
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试通过socket操作关闭连接
|
||||
if err := closeConnectionByAddresses(localAddr, remoteAddr); err != nil {
|
||||
fmt.Printf("Warning: failed to close connection %s->%s: %v\n", localAddr, remoteAddr, err)
|
||||
}
|
||||
// 再次确认是 socket
|
||||
if !strings.Contains(realPath, "socket:") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试关闭 fd
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Printf("Warning: panic closing %s: %v\n", fdPath, r)
|
||||
}
|
||||
}()
|
||||
f, err := os.OpenFile(fdPath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: open fd failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
// closeConnectionByAddresses 通过地址信息关闭连接
|
||||
func closeConnectionByAddresses(localAddr, remoteAddr string) error {
|
||||
// 解析本地地址
|
||||
localParts := strings.Split(localAddr, ":")
|
||||
if len(localParts) != 2 {
|
||||
return fmt.Errorf("invalid local address format")
|
||||
}
|
||||
|
||||
// 解析远程地址
|
||||
remoteParts := strings.Split(remoteAddr, ":")
|
||||
if len(remoteParts) != 2 {
|
||||
return fmt.Errorf("invalid remote address format")
|
||||
}
|
||||
|
||||
// 转换十六进制IP和端口
|
||||
localIP, err := hexToIP(localParts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localPort, err := strconv.ParseInt(localParts[1], 16, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteIP, err := hexToIP(remoteParts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remotePort, err := strconv.ParseInt(remoteParts[1], 16, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建连接并立即关闭来触发RST
|
||||
localAddr4 := fmt.Sprintf("%s:%d", localIP, localPort)
|
||||
remoteAddr4 := fmt.Sprintf("%s:%d", remoteIP, remotePort)
|
||||
|
||||
return sendRSTToConnection(localAddr4, remoteAddr4)
|
||||
}
|
||||
|
||||
// hexToIP 将十六进制IP地址转换为可读格式
|
||||
func hexToIP(hexIP string) (string, error) {
|
||||
if len(hexIP) == 8 { // IPv4
|
||||
ip := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
val, err := strconv.ParseUint(hexIP[i*2:(i+1)*2], 16, 8)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip[3-i] = byte(val) // 小端序
|
||||
}
|
||||
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]), nil
|
||||
}
|
||||
// 对于IPv6,处理更复杂,这里简化处理
|
||||
return "", fmt.Errorf("IPv6 not implemented yet")
|
||||
}
|
||||
|
||||
// sendRSTToConnection 向指定连接发送RST包
|
||||
func sendRSTToConnection(localAddr, remoteAddr string) error {
|
||||
// 尝试创建一个到远程地址的连接,然后立即使用SO_LINGER=0关闭
|
||||
// 这会发送RST包而不是正常的FIN包
|
||||
conn, err := net.Dial("tcp", remoteAddr)
|
||||
if err != nil {
|
||||
// 如果无法连接,说明连接可能已经不存在了
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取底层的TCP连接
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
conn.Close()
|
||||
return fmt.Errorf("not a TCP connection")
|
||||
}
|
||||
|
||||
// 设置SO_LINGER为0,这样关闭时会发送RST而不是FIN
|
||||
if err := tcpConn.SetLinger(0); err != nil {
|
||||
tcpConn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// 关闭连接,这会发送RST包
|
||||
return tcpConn.Close()
|
||||
}
|
||||
|
||||
@@ -274,10 +274,6 @@ func pauseServices(req pauseServicesRequest) error {
|
||||
return errors.New(fmt.Sprintf("service %s configuration not found", stp.name))
|
||||
}
|
||||
|
||||
// 使用和 updateService 相同的方法彻底断开所有连接
|
||||
stp.service.Close()
|
||||
registry.ServiceRegistry().Unregister(stp.name)
|
||||
|
||||
// 强制断开端口的所有连接
|
||||
if serviceConfig.Addr != "" {
|
||||
_ = kill.ForceClosePortConnections(serviceConfig.Addr)
|
||||
|
||||
Reference in New Issue
Block a user