继续修改竞态问题

This commit is contained in:
2026-08-11 21:31:56 +08:00
parent aebc4f4e58
commit 880a5d24ea
2 changed files with 44 additions and 35 deletions
+43 -34
View File
@@ -401,7 +401,9 @@ func (pm *ProcessManager) CheckPort() PortCheckResult {
}
conn.Close()
// 获取端口占用者信息
pid, process := pm.getPortOwner(pm.adminPort)
// 即使 pid=0,也返回 Ready=true,让调用方决定如何处理
return PortCheckResult{
Ready: true,
PID: pid,
@@ -410,10 +412,19 @@ func (pm *ProcessManager) CheckPort() PortCheckResult {
}
func (pm *ProcessManager) getPortOwner(port int) (int, string) {
if runtime.GOOS != "linux" {
return 0, ""
// 方法1: ss
if pid, name := pm.getPortOwnerBySS(port); pid > 0 {
return pid, name
}
// 方法2: netstat
if pid, name := pm.getPortOwnerByNetstat(port); pid > 0 {
return pid, name
}
return 0, ""
}
func (pm *ProcessManager) getPortOwnerBySS(port int) (int, string) {
// ss -lntp | grep ':7400 ' | grep -oP 'pid=\K[0-9]+' | head -1
cmd := exec.Command("sh", "-c", fmt.Sprintf("ss -lntp | grep ':%d ' | grep -oP 'pid=\\K[0-9]+' | head -1", port))
out, err := cmd.Output()
if err != nil {
@@ -427,30 +438,25 @@ func (pm *ProcessManager) getPortOwner(port int) (int, string) {
if err != nil || pid <= 0 {
return 0, ""
}
cmd2 := exec.Command("sh", "-c", fmt.Sprintf("ps -p %d -o comm= 2>/dev/null | head -1", pid))
out2, err2 := cmd2.Output()
if err2 != nil {
return pid, ""
}
return pid, strings.TrimSpace(string(out2))
return pid, ""
}
func (pm *ProcessManager) GetPortStatus() (*PortStatus, error) {
status := &PortStatus{Port: pm.adminPort, Occupied: false, PID: 0, IsFRPC: false}
result := pm.CheckPort()
if result.Err != nil {
return status, result.Err
func (pm *ProcessManager) getPortOwnerByNetstat(port int) (int, string) {
// netstat -tlnp | grep ':7400 ' | awk '{print $7}' | cut -d'/' -f1 | head -1
cmd := exec.Command("sh", "-c", fmt.Sprintf("netstat -tlnp 2>/dev/null | grep ':%d ' | awk '{print $7}' | cut -d'/' -f1 | head -1", port))
out, err := cmd.Output()
if err != nil {
return 0, ""
}
if !result.Ready {
return status, nil
pidStr := strings.TrimSpace(string(out))
if pidStr == "" {
return 0, ""
}
status.Occupied = true
status.PID = result.PID
if result.Process != "" {
status.IsFRPC = strings.Contains(result.Process, "frpc")
pid, err := strconv.Atoi(pidStr)
if err != nil || pid <= 0 {
return 0, ""
}
return status, nil
return pid, ""
}
// ================================================================
@@ -576,25 +582,33 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
return false
}
// 方式1: stdout 检测(快速通道
// stdout 快速通道
if strings.Contains(pm.getLastOutput(), "start proxy success") ||
strings.Contains(pm.getLastOutput(), "login to server success") {
log.Printf("[DEBUG] FRPReady(pid=%d): 检测到 stdout 关键字", pid)
return true
}
// 方式2: admin API 检测(绑定 PID
// 首先确认端口是否被当前 PID 占用
// 端口归属检测
portResult := pm.CheckPort()
if !portResult.Ready {
log.Printf("[DEBUG] FRPReady(pid=%d): 端口 %d 未就绪", pid, pm.adminPort)
return false
}
if portResult.PID > 0 && portResult.PID != pid {
log.Printf("[DEBUG] FRPReady(pid=%d): 端口被进程 %d 占用,与期望 PID %d 不一致", pid, portResult.PID, pid)
// 关键修复:端口被占用但无法识别归属 → 保守返回 false
if portResult.PID == 0 {
log.Printf("[DEBUG] FRPReady(pid=%d): 端口 %d 被占用但无法识别归属进程,保守返回 false", pid, pm.adminPort)
return false
}
if portResult.PID != pid {
log.Printf("[DEBUG] FRPReady(pid=%d): 端口 %d 被进程 %d 占用,与期望 PID %d 不一致",
pid, pm.adminPort, portResult.PID, pid)
return false
}
// admin API 检测
url := fmt.Sprintf("http://127.0.0.1:%d/api/status", pm.adminPort)
ctx, cancel := context.WithTimeout(context.Background(), APITimeout)
defer cancel()
@@ -628,7 +642,7 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
return false
}
// 检查是否有代理处于 running 状态
// 检查代理状态
proxyLists := [][]struct {
Name string `json:"name"`
Type string `json:"type"`
@@ -637,13 +651,8 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
LocalAddr string `json:"local_addr"`
RemoteAddr string `json:"remote_addr"`
}{
status.TCP,
status.UDP,
status.HTTP,
status.HTTPS,
status.STCP,
status.XTCP,
status.SUDP,
status.TCP, status.UDP, status.HTTP, status.HTTPS,
status.STCP, status.XTCP, status.SUDP,
}
for _, proxies := range proxyLists {