eithney commited on
Commit
04909fd
·
1 Parent(s): bc2665f

Enable port reuse in TCP server to prevent "Address already in use" errors

Browse files
Files changed (3) hide show
  1. PORT_MANAGEMENT.md +232 -0
  2. app.py +3 -0
  3. 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
+