close_wait的影响和消除

本文深入解析TCP连接中close_wait状态的原理,详细解释为什么会出现该状态,以及可能导致的原因,如服务端数据未处理完成或未调用close(socket)。同时提供了解决close_wait状态的方法,包括改善服务端代码和使用keepalive保活机制来检测和处理半关闭连接。

close_wait

close_wait是TCP结束连接四次握手时被动关闭的一端出现的状态。如下图所示:
这里写图片描述

为什么会出现保持在close_wait呢?很明显是被动关闭的一方未发FIN导致其一直处于CLOSE_WAIT。
那么为什么被动关闭的一方未发FIN了?很可能是被动关闭的一方还用数据没有发完,导致FIN没发。或者是
服务端没有调用TCP的close(socket)。

close_wait的影响

Close_Wait会占用一个连接,数量过多,就使可用的连接减少,还会并占用系统非换页内存。 而linux可用连接也是有限制的。如果不加处理,会极大的影响服务器的性能。

close_wait的解决方法

一是对服务端代码进行改善,查看服务端代码在处理完毕客户端请求,并在客户端数据处理完毕后,有没有进行使用close(socket)操作.
二是使用keepalive保活机制来对这种半关闭连接进行检测
int keepAlive = 1;//开启keepalive属性
int keepIdle = 60;//如果60秒内没有任何数据往来,则进行探测
int keepInterval = 6;//探测时发包的时间间隔为6秒
int keepCount = 3;//探测尝试的次数,如果第一次探测包就收到相应,以后的2次就不再发
//keepalive
setsockopt(m_iSock,SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(int));
setsockopt(m_iSock,SOL_TCP,TCP_KEEPIDLE,(void*)&keepIdle, sizeof(int));
setsockopt(m_iSock,SOL_TCP, TCP_KEEPINTVL,(void*)&keepInterval,sizeof(int));
setsockopt(m_iSock,SOL_TCP, TCP_KEEPCNT,(void*)&keepCount,sizeof(int));

""" 离线语音识别模块(基于 Vosk) 支持防自反馈机制:当 TTS 播放时不监听 ✅ 使用事件回调替代轮询 ✅ 消除全局单例副作用 ✅ 更优雅地管理资源超时 """ import threading import time import json import os import logging from vosk import Model as VoskModel, KaldiRecognizer import pyaudio from database.config import config from Progress.utils.logger_config import setup_logger from Progress.utils.resource_helper import get_internal_path logger = logging.getLogger("ai_assistant") # 默认模型路径 VOSK_MODEL_PATH = get_internal_path("models", "vosk-model-small-cn-0.22") TTS_WAIT_TIMEOUT = 60 # 最多等 60 秒 TTS 结束 class SpeechRecognizer: def __init__(self, tts_engine=None): self.tts_engine = tts_engine self.sample_rate = 16000 self.chunk_size = 1600 # 内部状态锁 self._state_lock = threading.Lock() self._is_tts_playing = False self._shutdown_event = threading.Event() # 控制整个生命周期 # 动态配置参数 self._current_timeout = config.get("voice_recognition", "timeout", "initial") self._min_volume_threshold = config.get("voice_recognition", "volume_threshold", "base") self._post_speech_short_wait = config.get("voice_recognition", "post_speech_short_wait", "value") self._post_speech_long_wait = config.get("voice_recognition", "post_speech_long_wait", "value") self.long_speech_threshold = config.get("voice_recognition", "long_speech_threshold", "value") # 音频与模型 self.model = None self.audio = None # PyAudio 实例 self._recognizer = None # KaldiRecognizer 流式引擎 # 注册到 TTS(如果提供) if tts_engine and hasattr(tts_engine, "register_playback_callback"): tts_engine.register_playback_callback( on_start=self._on_tts_start, on_stop=self._on_tts_stop ) else: logger.warning("⚠️ 未绑定 TTS 或 TTS 不支持播放回调") self._load_model() self._init_audio_system() logger.info("✅ 语音识别器初始化完成") @property def current_timeout(self): return self._current_timeout @current_timeout.setter def current_timeout(self, value): min_val = config.get("voice_recognition", "timeout", "min") max_val = config.get("voice_recognition", "timeout", "max") old = self._current_timeout self._current_timeout = max(min_val, min(max_val, float(value))) logger.debug(f"⏱️ 监听超时: {old:.1f} → {self._current_timeout:.1f}s") def _on_tts_start(self): """TTS 开始播放时调用""" with self._state_lock: self._is_tts_playing = True logger.debug("🔴 TTS 开始播放,暂停语音识别") def _on_tts_stop(self): """TTS 结束播放时调用""" with self._state_lock: self._is_tts_playing = False logger.debug("🟢 TTS 结束,恢复语音识别") @property def is_tts_playing(self): with self._state_lock: return self._is_tts_playing def _load_model(self): if not os.path.exists(VOSK_MODEL_PATH): raise FileNotFoundError(f"模型路径不存在: {VOSK_MODEL_PATH}") self.model = VoskModel(VOSK_MODEL_PATH) logger.info("✅ Vosk 模型加载成功") def _init_audio_system(self): try: self.audio = pyaudio.PyAudio() logger.debug("✅ PyAudio 初始化完成") except Exception as e: logger.exception("❌ 初始化音频系统失败") raise def _wait_for_tts_done(self, timeout=10): """ 等待 TTS 播放结束 :param timeout: 最大等待时间(秒) """ check_interval = 0.05 elapsed = 0 logger.debug(f"⏳ 正在等待 TTS 播放结束... 最多等待 {timeout}s") while self.is_tts_playing and not self._shutdown_event.is_set(): if elapsed >= timeout: logger.warning("🟡 等待 TTS 播放结束超时(%ds),强制继续", timeout) break time.sleep(check_interval) elapsed += check_interval if not self.is_tts_playing: logger.debug("✅ TTS 已结束,开始监听") else: logger.warning("⚠️ 强制继续,尽管 TTS 可能仍在播放") def listen_and_recognize(self, timeout=None) -> str: """ 听用户说话并返回识别文本 :param timeout: 可选超时时间(秒) :return: 识别出的中文文本(可能为空字符串) """ use_timeout = timeout or self.current_timeout min_t = config.get("voice_recognition", "timeout", "min") max_t = config.get("voice_recognition", "timeout", "max") use_timeout = max(min_t, min(max_t, float(use_timeout))) # 🔔 步骤1:等待当前 TTS 播放完成(事件驱动 + 超时保护) self._wait_for_tts_done() # 🔔 步骤2:检查是否已被关闭 if self._shutdown_event.is_set(): logger.debug("🛑 录音被中止:系统正在退出") return "" stream = None try: # 创建新的识别器(每次独立) self._recognizer = KaldiRecognizer(self.model, self.sample_rate) stream = self.audio.open( format=pyaudio.paInt16, channels=1, rate=self.sample_rate, input=True, frames_per_buffer=self.chunk_size, input_device_index=None # 使用默认设备 ) start_time = time.time() in_speech = False final_text = "" logger.info("🎙️ 请说话...") while (time.time() - start_time) < use_timeout: # 外部中断检查 if self._shutdown_event.is_set(): logger.debug("🛑 录音被外部中断") break # 再次确认 TTS 是否突然开始 if self.is_tts_playing: logger.info("🔇 TTS 正在播放,中断本次识别") break # 读取音频块 try: data = stream.read(self.chunk_size, exception_on_overflow=False) except Exception as e: logger.warning(f"音频读取失败: {e}") continue # 流式识别处理 if self._recognizer.AcceptWaveform(data): result_json = self._recognizer.Result() result_dict = json.loads(result_json) text = result_dict.get("text", "").strip() if text: final_text = text duration = time.time() - start_time logger.info(f"✅ 识别完成 ({duration:.1f}s): '{final_text}'") break else: partial_result = json.loads(self._recognizer.PartialResult()) partial_text = partial_result.get("partial", "").strip() if partial_text and not in_speech: in_speech = True logger.debug(f"🎤 检测到语音输入: '{partial_text}'") return final_text.strip() except OSError as e: if "No default input device" in str(e): logger.error("❌ 未检测到麦克风,请检查设备连接") else: logger.exception("🔴 音频设备错误") return "" except Exception as e: logger.exception("🔴 语音识别过程发生未知异常") return "" finally: # 安全释放资源 if stream: try: stream.stop_stream() stream.close() except Exception as e: logger.warning(f"关闭音频流失败: {e}") self._recognizer = None # 清理引用 def close(self): """释放所有资源""" self._shutdown_event.set() if self.audio: try: self.audio.terminate() logger.debug("⏹️ PyAudio 已终止") except Exception as e: logger.warning(f"PyAudio terminate 失败: {e}") def shutdown(self): """别名:兼容旧代码""" self.close() 由于声音监测有过滤,在音乐播放时会对用户声音失去敏感度。也就是噪音盖过主音,怎么解决
最新发布
10-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值