继续修改竞态问题
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build \
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
# 先切换到国内镜像源,再安装包
|
# 先切换到国内镜像源,再安装包
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
RUN apk --no-cache add ca-certificates tzdata sqlite bash curl iproute2
|
RUN apk --no-cache add ca-certificates tzdata sqlite bash curl iproute2 net-tools
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
+43
-34
@@ -401,7 +401,9 @@ func (pm *ProcessManager) CheckPort() PortCheckResult {
|
|||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
|
||||||
|
// 获取端口占用者信息
|
||||||
pid, process := pm.getPortOwner(pm.adminPort)
|
pid, process := pm.getPortOwner(pm.adminPort)
|
||||||
|
// 即使 pid=0,也返回 Ready=true,让调用方决定如何处理
|
||||||
return PortCheckResult{
|
return PortCheckResult{
|
||||||
Ready: true,
|
Ready: true,
|
||||||
PID: pid,
|
PID: pid,
|
||||||
@@ -410,10 +412,19 @@ func (pm *ProcessManager) CheckPort() PortCheckResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProcessManager) getPortOwner(port int) (int, string) {
|
func (pm *ProcessManager) getPortOwner(port int) (int, string) {
|
||||||
if runtime.GOOS != "linux" {
|
// 方法1: ss
|
||||||
return 0, ""
|
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))
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("ss -lntp | grep ':%d ' | grep -oP 'pid=\\K[0-9]+' | head -1", port))
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -427,30 +438,25 @@ func (pm *ProcessManager) getPortOwner(port int) (int, string) {
|
|||||||
if err != nil || pid <= 0 {
|
if err != nil || pid <= 0 {
|
||||||
return 0, ""
|
return 0, ""
|
||||||
}
|
}
|
||||||
|
return pid, ""
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProcessManager) GetPortStatus() (*PortStatus, error) {
|
func (pm *ProcessManager) getPortOwnerByNetstat(port int) (int, string) {
|
||||||
status := &PortStatus{Port: pm.adminPort, Occupied: false, PID: 0, IsFRPC: false}
|
// netstat -tlnp | grep ':7400 ' | awk '{print $7}' | cut -d'/' -f1 | head -1
|
||||||
result := pm.CheckPort()
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("netstat -tlnp 2>/dev/null | grep ':%d ' | awk '{print $7}' | cut -d'/' -f1 | head -1", port))
|
||||||
if result.Err != nil {
|
out, err := cmd.Output()
|
||||||
return status, result.Err
|
if err != nil {
|
||||||
|
return 0, ""
|
||||||
}
|
}
|
||||||
if !result.Ready {
|
pidStr := strings.TrimSpace(string(out))
|
||||||
return status, nil
|
if pidStr == "" {
|
||||||
|
return 0, ""
|
||||||
}
|
}
|
||||||
status.Occupied = true
|
pid, err := strconv.Atoi(pidStr)
|
||||||
status.PID = result.PID
|
if err != nil || pid <= 0 {
|
||||||
if result.Process != "" {
|
return 0, ""
|
||||||
status.IsFRPC = strings.Contains(result.Process, "frpc")
|
|
||||||
}
|
}
|
||||||
return status, nil
|
return pid, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
@@ -576,25 +582,33 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方式1: stdout 检测(快速通道)
|
// stdout 快速通道
|
||||||
if strings.Contains(pm.getLastOutput(), "start proxy success") ||
|
if strings.Contains(pm.getLastOutput(), "start proxy success") ||
|
||||||
strings.Contains(pm.getLastOutput(), "login to server success") {
|
strings.Contains(pm.getLastOutput(), "login to server success") {
|
||||||
log.Printf("[DEBUG] FRPReady(pid=%d): 检测到 stdout 关键字", pid)
|
log.Printf("[DEBUG] FRPReady(pid=%d): 检测到 stdout 关键字", pid)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方式2: admin API 检测(绑定 PID)
|
// 端口归属检测
|
||||||
// 首先确认端口是否被当前 PID 占用
|
|
||||||
portResult := pm.CheckPort()
|
portResult := pm.CheckPort()
|
||||||
if !portResult.Ready {
|
if !portResult.Ready {
|
||||||
log.Printf("[DEBUG] FRPReady(pid=%d): 端口 %d 未就绪", pid, pm.adminPort)
|
log.Printf("[DEBUG] FRPReady(pid=%d): 端口 %d 未就绪", pid, pm.adminPort)
|
||||||
return false
|
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
|
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)
|
url := fmt.Sprintf("http://127.0.0.1:%d/api/status", pm.adminPort)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), APITimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), APITimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -628,7 +642,7 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有代理处于 running 状态
|
// 检查代理状态
|
||||||
proxyLists := [][]struct {
|
proxyLists := [][]struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@@ -637,13 +651,8 @@ func (pm *ProcessManager) isFRPReady(pid int) bool {
|
|||||||
LocalAddr string `json:"local_addr"`
|
LocalAddr string `json:"local_addr"`
|
||||||
RemoteAddr string `json:"remote_addr"`
|
RemoteAddr string `json:"remote_addr"`
|
||||||
}{
|
}{
|
||||||
status.TCP,
|
status.TCP, status.UDP, status.HTTP, status.HTTPS,
|
||||||
status.UDP,
|
status.STCP, status.XTCP, status.SUDP,
|
||||||
status.HTTP,
|
|
||||||
status.HTTPS,
|
|
||||||
status.STCP,
|
|
||||||
status.XTCP,
|
|
||||||
status.SUDP,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, proxies := range proxyLists {
|
for _, proxies := range proxyLists {
|
||||||
|
|||||||
Reference in New Issue
Block a user