Spaces:
Sleeping
Sleeping
eithney commited on
Commit ·
04909fd
1
Parent(s): bc2665f
Enable port reuse in TCP server to prevent "Address already in use" errors
Browse files- PORT_MANAGEMENT.md +232 -0
- app.py +3 -0
- kill_port.sh +41 -0
PORT_MANAGEMENT.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 端口管理指南
|
| 2 |
+
|
| 3 |
+
## 端口占用问题说明
|
| 4 |
+
|
| 5 |
+
### 常见原因
|
| 6 |
+
|
| 7 |
+
1. **进程未完全退出**
|
| 8 |
+
- 使用 `Ctrl+C` 后进程可能仍在后台运行
|
| 9 |
+
- 子进程没有被正确清理
|
| 10 |
+
|
| 11 |
+
2. **TCP TIME_WAIT 状态**
|
| 12 |
+
- TCP连接关闭后会进入 TIME_WAIT 状态
|
| 13 |
+
- 通常持续 30-120 秒
|
| 14 |
+
- 这是 TCP 协议的正常行为
|
| 15 |
+
|
| 16 |
+
3. **多个实例运行**
|
| 17 |
+
- 启动了多个程序实例
|
| 18 |
+
- 没有正确关闭之前的实例
|
| 19 |
+
|
| 20 |
+
## 解决方案
|
| 21 |
+
|
| 22 |
+
### 方法1: 使用提供的脚本(推荐)
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
# 清理7860端口
|
| 26 |
+
./kill_port.sh 7860
|
| 27 |
+
|
| 28 |
+
# 或清理其他端口
|
| 29 |
+
./kill_port.sh 8000
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
### 方法2: 手动清理
|
| 33 |
+
|
| 34 |
+
#### 查找占用端口的进程
|
| 35 |
+
```bash
|
| 36 |
+
# 方法1: 使用 lsof
|
| 37 |
+
sudo lsof -i:7860
|
| 38 |
+
|
| 39 |
+
# 方法2: 使用 netstat
|
| 40 |
+
sudo netstat -tlnp | grep 7860
|
| 41 |
+
|
| 42 |
+
# 方法3: 使用 ss
|
| 43 |
+
sudo ss -tlnp | grep 7860
|
| 44 |
+
|
| 45 |
+
# 方法4: 使用 fuser
|
| 46 |
+
sudo fuser 7860/tcp
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
#### 杀死进程
|
| 50 |
+
```bash
|
| 51 |
+
# 如果知道进程ID (PID)
|
| 52 |
+
kill -9 <PID>
|
| 53 |
+
|
| 54 |
+
# 一行命令清理端口
|
| 55 |
+
sudo lsof -ti:7860 | xargs kill -9
|
| 56 |
+
|
| 57 |
+
# 或使用 fuser
|
| 58 |
+
sudo fuser -k 7860/tcp
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
### 方法3: 使用改进后的 app.py
|
| 62 |
+
|
| 63 |
+
新版本的 `app.py` 已经实现了:
|
| 64 |
+
|
| 65 |
+
1. **端口复用** (`SO_REUSEADDR` 和 `SO_REUSEPORT`)
|
| 66 |
+
- 避免 "Address already in use" 错误
|
| 67 |
+
- 允许快速重启服务
|
| 68 |
+
|
| 69 |
+
2. **信号处理**
|
| 70 |
+
- 捕获 `SIGINT` (Ctrl+C) 和 `SIGTERM` 信号
|
| 71 |
+
- 优雅关闭服务器,释放端口
|
| 72 |
+
|
| 73 |
+
3. **自动端口清理**
|
| 74 |
+
- 启动时检查端口占用
|
| 75 |
+
- 尝试自动清理占用的进程
|
| 76 |
+
|
| 77 |
+
## 预防措施
|
| 78 |
+
|
| 79 |
+
### 1. 正确停止服务器
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
# 使用 Ctrl+C 停止
|
| 83 |
+
# 程序会捕获信号并优雅退出
|
| 84 |
+
|
| 85 |
+
# 或发送 SIGTERM 信号
|
| 86 |
+
kill -15 <PID>
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
### 2. 使用进程管理器
|
| 90 |
+
|
| 91 |
+
#### systemd(生产环境推荐)
|
| 92 |
+
```bash
|
| 93 |
+
# 创建 systemd 服务文件
|
| 94 |
+
sudo nano /etc/systemd/system/learning-app.service
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
```ini
|
| 98 |
+
[Unit]
|
| 99 |
+
Description=Interactive Learning App
|
| 100 |
+
After=network.target
|
| 101 |
+
|
| 102 |
+
[Service]
|
| 103 |
+
Type=simple
|
| 104 |
+
User=zhangl
|
| 105 |
+
WorkingDirectory=/data/zhangl/code/hf/point
|
| 106 |
+
ExecStart=/usr/bin/python3 /data/zhangl/code/hf/point/app.py
|
| 107 |
+
Restart=on-failure
|
| 108 |
+
KillMode=mixed
|
| 109 |
+
KillSignal=SIGTERM
|
| 110 |
+
TimeoutStopSec=5
|
| 111 |
+
|
| 112 |
+
[Install]
|
| 113 |
+
WantedBy=multi-user.target
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
```bash
|
| 117 |
+
# 启用并启动服务
|
| 118 |
+
sudo systemctl daemon-reload
|
| 119 |
+
sudo systemctl enable learning-app
|
| 120 |
+
sudo systemctl start learning-app
|
| 121 |
+
|
| 122 |
+
# 管理服务
|
| 123 |
+
sudo systemctl stop learning-app # 停止
|
| 124 |
+
sudo systemctl restart learning-app # 重启
|
| 125 |
+
sudo systemctl status learning-app # 查看状态
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
#### supervisor(备选方案)
|
| 129 |
+
```ini
|
| 130 |
+
[program:learning-app]
|
| 131 |
+
directory=/data/zhangl/code/hf/point
|
| 132 |
+
command=python3 app.py
|
| 133 |
+
autostart=true
|
| 134 |
+
autorestart=true
|
| 135 |
+
user=zhangl
|
| 136 |
+
redirect_stderr=true
|
| 137 |
+
stdout_logfile=/var/log/learning-app.log
|
| 138 |
+
stopasgroup=true
|
| 139 |
+
killasgroup=true
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
### 3. 使用虚拟终端
|
| 143 |
+
|
| 144 |
+
```bash
|
| 145 |
+
# 使用 screen
|
| 146 |
+
screen -S learning-app
|
| 147 |
+
python3 app.py
|
| 148 |
+
# 按 Ctrl+A 然后 D 分离会话
|
| 149 |
+
|
| 150 |
+
# 重新连接
|
| 151 |
+
screen -r learning-app
|
| 152 |
+
|
| 153 |
+
# 使用 tmux
|
| 154 |
+
tmux new -s learning-app
|
| 155 |
+
python3 app.py
|
| 156 |
+
# 按 Ctrl+B 然后 D 分离会话
|
| 157 |
+
|
| 158 |
+
# 重新连接
|
| 159 |
+
tmux attach -t learning-app
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## 常用命令汇总
|
| 163 |
+
|
| 164 |
+
```bash
|
| 165 |
+
# 查看所有监听的端口
|
| 166 |
+
sudo netstat -tlnp
|
| 167 |
+
|
| 168 |
+
# 查看所有Python进程
|
| 169 |
+
ps aux | grep python
|
| 170 |
+
|
| 171 |
+
# 查看特定用户的进程
|
| 172 |
+
ps aux | grep zhangl
|
| 173 |
+
|
| 174 |
+
# 杀死所有Python进程(危险!)
|
| 175 |
+
# pkill -9 python3
|
| 176 |
+
|
| 177 |
+
# 查看端口连接状态
|
| 178 |
+
sudo netstat -an | grep 7860
|
| 179 |
+
|
| 180 |
+
# 测试端口是否可访问
|
| 181 |
+
nc -zv localhost 7860
|
| 182 |
+
telnet localhost 7860
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
## 故障排除
|
| 186 |
+
|
| 187 |
+
### 问题1: 端口仍被占用
|
| 188 |
+
```bash
|
| 189 |
+
# 使用 root 权限清理
|
| 190 |
+
sudo lsof -ti:7860 | xargs sudo kill -9
|
| 191 |
+
|
| 192 |
+
# 或重启网络服务
|
| 193 |
+
sudo systemctl restart networking
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
### 问题2: Permission denied
|
| 197 |
+
```bash
|
| 198 |
+
# 确保脚本有执行权限
|
| 199 |
+
chmod +x kill_port.sh
|
| 200 |
+
|
| 201 |
+
# 使用 sudo 运行
|
| 202 |
+
sudo ./kill_port.sh 7860
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
### 问题3: 找不到占用进程
|
| 206 |
+
```bash
|
| 207 |
+
# 可能是僵尸进程,尝试
|
| 208 |
+
sudo kill -9 $(ps aux | grep 'defunct' | awk '{print $2}')
|
| 209 |
+
|
| 210 |
+
# 或重启系统(最后手段)
|
| 211 |
+
sudo reboot
|
| 212 |
+
```
|
| 213 |
+
|
| 214 |
+
## 监控端口使用
|
| 215 |
+
|
| 216 |
+
```bash
|
| 217 |
+
# 实时监控端口
|
| 218 |
+
watch -n 1 'sudo lsof -i:7860'
|
| 219 |
+
|
| 220 |
+
# 记录端口使用日志
|
| 221 |
+
while true; do
|
| 222 |
+
echo "$(date): $(sudo lsof -i:7860 | wc -l) connections"
|
| 223 |
+
sleep 5
|
| 224 |
+
done >> port_monitor.log
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
## 参考资料
|
| 228 |
+
|
| 229 |
+
- [TCP TIME_WAIT 状态详解](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_termination)
|
| 230 |
+
- [SO_REUSEADDR vs SO_REUSEPORT](https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ)
|
| 231 |
+
- [Linux 信号处理](https://man7.org/linux/man-pages/man7/signal.7.html)
|
| 232 |
+
|
app.py
CHANGED
|
@@ -65,6 +65,9 @@ def main():
|
|
| 65 |
port = int(os.environ.get('PORT', 7860))
|
| 66 |
|
| 67 |
try:
|
|
|
|
|
|
|
|
|
|
| 68 |
# 创建服务器
|
| 69 |
with socketserver.TCPServer(("0.0.0.0", port), CORSHTTPRequestHandler) as httpd:
|
| 70 |
print(f"📱 服务器启动成功")
|
|
|
|
| 65 |
port = int(os.environ.get('PORT', 7860))
|
| 66 |
|
| 67 |
try:
|
| 68 |
+
# 设置端口重用选项,避免 "Address already in use" 错误
|
| 69 |
+
socketserver.TCPServer.allow_reuse_address = True
|
| 70 |
+
|
| 71 |
# 创建服务器
|
| 72 |
with socketserver.TCPServer(("0.0.0.0", port), CORSHTTPRequestHandler) as httpd:
|
| 73 |
print(f"📱 服务器启动成功")
|
kill_port.sh
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# 端口清理工具
|
| 3 |
+
# 使用方法: ./kill_port.sh [端口号]
|
| 4 |
+
|
| 5 |
+
PORT=${1:-7860}
|
| 6 |
+
|
| 7 |
+
echo "🔍 检查端口 $PORT 的占用情况..."
|
| 8 |
+
|
| 9 |
+
# 查找占用端口的进程
|
| 10 |
+
PID=$(lsof -ti:$PORT 2>/dev/null)
|
| 11 |
+
|
| 12 |
+
if [ -z "$PID" ]; then
|
| 13 |
+
echo "✅ 端口 $PORT 当前未被占用"
|
| 14 |
+
exit 0
|
| 15 |
+
fi
|
| 16 |
+
|
| 17 |
+
echo "⚠️ 发现进程占用端口 $PORT"
|
| 18 |
+
echo "进程信息:"
|
| 19 |
+
lsof -i:$PORT
|
| 20 |
+
|
| 21 |
+
echo ""
|
| 22 |
+
read -p "是否要杀死这些进程? (y/N): " -n 1 -r
|
| 23 |
+
echo
|
| 24 |
+
|
| 25 |
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
| 26 |
+
echo "🔫 正在杀死进程..."
|
| 27 |
+
kill -9 $PID
|
| 28 |
+
sleep 1
|
| 29 |
+
|
| 30 |
+
# 再次检查
|
| 31 |
+
NEW_PID=$(lsof -ti:$PORT 2>/dev/null)
|
| 32 |
+
if [ -z "$NEW_PID" ]; then
|
| 33 |
+
echo "✅ 端口 $PORT 已成功释放"
|
| 34 |
+
else
|
| 35 |
+
echo "❌ 端口 $PORT 仍被占用,可能需要 root 权限"
|
| 36 |
+
echo "💡 尝试运行: sudo ./kill_port.sh $PORT"
|
| 37 |
+
fi
|
| 38 |
+
else
|
| 39 |
+
echo "❌ 操作已取消"
|
| 40 |
+
fi
|
| 41 |
+
|