Linux socket编程: xxx: Address already in use

本文详细探讨了在Linux环境下,TCP服务器进程在强制停止后,端口因TIME_WAIT状态导致无法立即复用的问题。通过设置SO_REUSEADDR选项,实现端口快速重用,提高开发效率。

最近在做linux上的socket的TCP服务器设计,碰到一个问题: 服务器进程bind并监听了一个端口A,然后进程被强制停止了,然后重启进程,出现了’xxx: Address already in use’错误,要等几分钟才能重新bind和监听端口A.在网上找到了下面这篇博文,并解决了这个问题,在此感谢博主.

1 发现问题  

  我在开发一个socket服务器程序并反复调试的时候,发现了一个让人无比心烦的情况:每次kill掉该服务器进程并重新启动的时候,都会出现bind错误:error:98,Address already in use。然而再kill掉该进程,再次重新启动的时候,就bind成功了。真让人摸不着头脑。难道一定要尝试两次才显得真诚?这不科学!

  我的第一反应是kill进程的时候,并没有完全释放掉socket资源,倒致第二次启动的时候,bind失败。那么第三次怎么又成功了呢?

  查资料:有人说是TIME_WAIT在捣鬼。

  回想一下,Linux下的TIME_WAIT大概是2分钟,这样也合情合理。那么没有释放掉的资源是什么呢,是端口吗?机智的我立刻决定做实验找出答案。启动服务器程序,在与客户建立连接之后,kill掉服务器。飞快地在terminal里输入命令:netstat -an|grep 9877。这里9877是我服务器打算绑定的端口。果然:

  

  结果显示9877端口正在被使用,并处于TCP中的TIME_WAIT状态。再过两分钟,我再执行命令netstat -an|grep 9877,世界清静了,什么都没有。

  终于找到了答案:果然是TIME_WAIT在捣鬼。

2 解决问题

  问题找到了,可是怎么解决问题呢。如何才能结束掉这个TIME_WAIT状态呢?否则每次调试之后,都要巴巴地等上两分钟,再进行下次调试。这太蠢了!想了好久,也没想出解决办法。那TCP中有没有能关闭掉TIME_WAIT的选项呢?翻书!UNP中第7章就是讲socket选项的。还真没有找到。但是,我找到了SO_REUSEADDR选项。关于此选项,书上说可以起到以下4个不同的功用:

  (1)SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作他们的本地端口的连接仍存在。

  (2)允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。

  (3)SO_REUSEADDR 允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。

  (4)SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口号已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说本特性仅支持UDP套接字。

  我遇到的情况正好符合情况1,并且书上说了:“所有TCP服务器都应该指定本套接字选项,以允许服务器在这种情形下被重新启动。”那么试试看喽。

  

      上面两行代码,把此套接字listenFd设置为允许地址重用(on=1,如果on=0就是不允许重用了)。这样每次bind的时候,如果此端口正在使用的话,bind就会把端口“抢”过来。就不会报错了。完美解决问题。

3 察漏补缺

  既然TIME_WAIT这么讨厌,那它的存在有什么意义呢?毕竟服务器端已经中断掉连接了呀。记得之前在看UNP的时候,上面好像有提到过,继续翻书:

  书上说,TIME_WAIT状态有两个存在的理由:

  (1)可靠地实现TCP全双工连接的终止;

  (2)允许老的重复分节在网络中消逝。

  原来如此,解释一下,上个图:

  

  (1)如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。

  (2)如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。

  回到我们的问题,由于我并不是正常地经过四次断开的方式中断连接,所以并不会存在最后一个ACK的问题。所以,这样是安全的。不过,最终的服务器版本,还是不要设置为端口可复用的。切记。

  加油加油。好好学习,天天向上。

  另外:UNP是本好书,要好好看呀。

zzc@zzc-IdeaPad-15sALC-2021:~/demo/myproject$ uwsgi --ini myproject_uwsgi.ini [uWSGI] getting INI configuration from myproject_uwsgi.ini *** Starting uWSGI 2.0.30 (64bit) on [Fri Aug 15 14:33:49 2025] *** compiled with version: 11.4.0 on 14 August 2025 07:43:17 os: Linux-6.8.0-65-generic #68~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 15 18:06:34 UTC 2 nodename: zzc-IdeaPad-15sALC-2021 machine: x86_64 clock source: unix pcre jit disabled detected number of CPU cores: 12 current working directory: /home/zzc/demo/myproject detected binary path: /usr/local/bin/uwsgi chdir() to /home/zzc/demo/myproject your processes number limit is 71053 your memory page size is 4096 bytes detected max file descriptor number: 1024 lock engine: pthread robust mutexes thunder lock: disabled (you can enable it with --thunder-lock) uwsgi socket 0 bound to UNIX address /home/zzc/demo/myproject/myproject.sock fd 3 Python version: 3.10.12 (main, May 27 2025, 17:12:29) [GCC 11.4.0] Python main interpreter initialized at 0x62d08a254ce0 python threads support enabled your server socket listen backlog is limited to 100 connections your mercy for graceful operations on workers is 60 seconds mapped 364600 bytes (356 KB) for 4 cores *** Operational MODE: preforking *** WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x62d08a254ce0 pid: 9507 (default app) *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 9507) spawned uWSGI worker 1 (pid: 9509, cores: 1) spawned uWSGI worker 2 (pid: 9510, cores: 1) spawned uWSGI worker 3 (pid: 9511, cores: 1) spawned uWSGI worker 4 (pid: 9512, cores: 1) 输出是这个运行成功了么?
最新发布
08-16
<think>我们正在讨论如何判断uWSGI是否成功启动。根据用户的问题,他需要根据日志输出来分析uWSGI的启动状态。以下是我的回答: ### 如何通过日志判断uWSGI启动成功 1. **查看uWSGI的日志输出**: uWSGI在启动时会输出详细的日志信息,可以通过以下方式获取: - 如果直接在终端运行,日志会输出到终端。 - 如果配置了日志文件(在ini文件中通过`daemonize`或`logto`指定),则查看该日志文件。 成功启动的典型日志特征: - 出现`spawned uWSGI master process`表示主进程已启动[^1]。 - 接着出现`spawned uWSGI worker processes`表示工作进程已启动[^1]。 - 最后出现`WSGI app 0 (mountpoint='') ready in X seconds`表示应用加载完成[^1]。 示例成功日志片段: ``` *** Starting uWSGI 2.0.20 (64bit) on [Thu Mar 24 15:00:00 2022] *** compiled with version: ... on ... os: Linux-5.4.0-100-generic #113-Ubuntu SMP *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 12345) spawned uWSGI worker 1 (pid: 12346, cores: 1) spawned uWSGI worker 2 (pid: 12347, cores: 1) WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x... ``` 2. **检查错误日志**: 如果启动失败,日志中会包含错误信息,常见错误包括: - **应用加载失败**:如Python模块导入错误、Django设置错误等,会显示`ImportError`或`ModuleNotFoundError`等异常[^1]。 - **端口/Socket冲突**:如`bind(): Address already in use`表示端口被占用[^3]。 - **权限问题**:如`Permission denied`表示无权访问Socket或日志路径[^3]。 3. **通过进程状态确认**: 使用`ps`命令检查uWSGI进程是否存在: ```bash ps aux | grep uwsgi ``` 正常情况应包含主进程(master)和至少一个工作进程(worker)[^1]。 4. **监听端口/Socket验证**: - 如果配置了HTTP端口(如`http = :8000`),使用`curl`测试: ```bash curl http://localhost:8000 ``` - 如果使用Unix Socket(如`socket = /tmp/myproject.sock`),检查Socket文件是否存在: ```bash ls -l /tmp/myproject.sock ``` ### 日志配置建议 在`myproject_uwsgi.ini`中配置日志路径,便于长期监控: ```ini ; 将所有日志输出到文件 daemonize = /var/log/uwsgi/myproject.log ; 或仅记录错误日志 ; logto = /var/log/uwsgi/error.log ``` ### 常见问题处理 - **启动后立即退出**:检查应用初始化代码是否有异常退出逻辑。 - **工作进程反复重启**:通常由应用运行时错误(如Django中间件异常)导致,需检查应用日志。 - **权限错误**:确保uWSGI进程用户有权访问项目目录和Socket路径[^3]。 --- §§相关问题§§ 1. uWSGI启动时如何配置日志文件路径? 2. 如何解决uWSGI端口被占用的问题? 3. 如何设置uWSGI以系统服务(systemd)方式运行?</think>### 判断 uWSGI 启动是否成功的日志分析方法 #### 一、核心成功标志(关键日志行) 1. **主进程和工作进程启动成功** ```log spawned uWSGI master process (pid: XXX) # 主进程启动 [^1] spawned uWSGI worker X (pid: YYY, cores: Z) # 工作进程启动 [^1] ``` 2. **应用加载完成** ```log WSGI app 0 (mountpoint='') ready in X seconds # 应用就绪 [^1] ``` 3. **Socket/端口监听成功** ```log bound to TCP address 0.0.0.0:8000 fd 4 # HTTP端口监听 bound to UNIX socket /run/uwsgi/app.sock fd 5 # Unix socket监听 [^3] ``` #### 二、失败特征及诊断方法 | **错误类型** | **典型日志内容** | **解决方案** | |--------------------|------------------------------------------|--------------------------------| | **端口冲突** | `bind(): Address already in use (98)` | 更换端口或杀死占用进程 `lsof -i:端口` [^3] | | **权限问题** | `Permission denied` | 检查目录权限和用户归属 | | **模块加载失败** | `ImportError: No module named 'xxx'` | 检查 Python 虚拟环境和依赖 | | **配置错误** | `invalid mountpoint/unknown option` | 验证 INI 文件语法和路径正确性 | | **应用初始化崩溃** | `Segmentation fault/core dumped` | 检查应用代码和资源加载逻辑 | #### 三、验证操作流程 1. **实时日志监控** ```bash tail -f /var/log/uwsgi/app.log # 查看实时日志 ``` 2. **进程状态检查** ```bash ps aux | grep uwsgi # 应看到 master + worker 进程 # 示例输出: # www-data 1234 0.0 1.2 /usr/bin/uwsgi --ini app.ini (master) # www-data 1235 2.1 3.4 /usr/bin/uwsgi --ini app.ini (worker 1) ``` 3. **端口/Socket 验证** ```bash ss -ltnp | grep uwsgi # 检查监听端口 ls -l /run/uwsgi/*.sock # 检查 Socket 文件权限 [^3] ``` 4. **HTTP 服务测试** ```bash curl -I http://localhost:8000 # 返回 HTTP 200/302 即成功 ``` #### 四、日志配置建议(`myproject_uwsgi.ini`) ```ini [uwsgi] ; 关键日志配置 logto = /var/log/uwsgi/app.log ; 主日志路径 log-route = .* /var/log/uwsgi/debug.log ; 调试日志 disable-logging = false ; 确保日志启用 ``` > **注**:若日志中连续出现 `worker respawning too fast` 表示工作进程崩溃,需检查应用代码或资源限制 [^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值