https://www.3cx.com/pbx/what-is-a-stun-server/

A STUN (Session Traversal of User Datagram Protocol [UDP] Through Network Address Translators [NATs]) server allows NAT clients (i.e. IP Phones behind a firewall) to set up phone calls to a VoIP provider hosted outside of the local network.

STUN Server Diagram
The STUN server allows clients to find out their public address, the type of NAT they are behind and the Internet side port associated by the NAT with a particular local port. This information is used to set up UDP communication between the client and the VoIP provider to establish a call. The STUN protocol is defined in RFC 3489.

The STUN server is contacted on UDP port 3478, however, the server will hint clients to perform tests on alternate IP and port number too (STUN servers have two IP addresses). The RFC states that this port and IP are arbitrary.

Stun functionality is seamlessly handled by 3CX - an easy to install PBX.

中文解释:
https://mp.weixin.qq.com/s/cQgli4y-sLNg918MKW8D0g

说人话就是:
白嫖公开的STUN服务器,用它的UDP端口,走流量,打洞。

内网机器往 stun4.l.google.com(142.251.2.127) 发送了一个 STUN Binding 的包,这是通过 UDP 传输的,所以这个请求会在运营商里面打开一个临时的 UDP 通道。

内网机器就是通过这个 临时的 UDP 通道 跟 STUN 服务器通信。所以这个 UDP 通道的信息,STUN 服务器是知道的。因此 STUN 服务器可以把这个 UDP 通道信息 放在 STUN Response 里面返回给 内网的机器。

3918,唐僧,183,NULL,,
3919,"孙悟空,猪八戒",176,NULL,,

如果用split(","),是会出错的。两种办法
1,

import csv
from io import StringIO

line = '3919,"孙悟空,猪八戒",176,NULL,,'

# 将文本包装在 StringIO 对象中,以便 csv 模块可以处理字符串
csv_data = StringIO(line)

# 创建 csv 读取器
reader = csv.reader(csv_data)

# 读取一行数据
result = next(reader)

print(result)

2,

import re

line = '3919,"孙悟空,猪八戒",176,NULL,,'

# 定义正则表达式模式,匹配双引号内的内容或者逗号分隔的内容
pattern = re.compile(r'("(?:[^"]|"")*"|[^,]*)')

# 使用 findall() 方法查找匹配的内容,并去除双引号
result = [m.strip('"') for m in pattern.findall(line)]

print(result)

import pyaudio
import wave
import threading
import time

class Recorder:
    def __init__(self, filename='recording.wav', chunk=1024, format=pyaudio.paInt16, channels=2, rate=44100):
        self.filename = filename
        self.chunk = chunk
        self.format = format
        self.channels = channels
        self.rate = rate
        self.frames = []
        self.p = pyaudio.PyAudio()
        self.is_recording = False
        self.record_thread = None

    def start_recording(self):
        if self.is_recording:
            print("Recording is already in progress.")
            return

        print("Recording started.")
        self.is_recording = True
        self.frames = []
        self.stream = self.p.open(format=self.format,
                                  channels=self.channels,
                                  rate=self.rate,
                                  input=True,
                                  frames_per_buffer=self.chunk)
        self.record_thread = threading.Thread(target=self._record)
        self.record_thread.start()

    def stop_recording(self):
        if not self.is_recording:
            print("No recording in progress.")
            return

        print("Recording stopped.")
        self.is_recording = False
        self.stream.stop_stream()
        self.stream.close()
        self.record_thread.join()
        wf = wave.open(self.filename, 'wb')
        wf.setnchannels(self.channels)
        wf.setsampwidth(self.p.get_sample_size(self.format))
        wf.setframerate(self.rate)
        wf.writeframes(b''.join(self.frames))
        wf.close()

    def _record(self):
        while self.is_recording:
            data = self.stream.read(self.chunk)
            self.frames.append(data)

if __name__ == "__main__":
    filename = input("Enter the filename to save recording (default is 'recording.wav'): ").strip() or 'recording.wav'
    recorder = Recorder(filename=filename)
    
    print("Press 'r' to start recording, 's' to stop recording, and 'q' to quit.")
    while True:
        command = input("> ").strip().lower()
        if command == 'r':
            recorder.start_recording()
        elif command == 's':
            recorder.stop_recording()
            print("Recording saved to", filename)
        elif command == 'q':
            if recorder.is_recording:
                recorder.stop_recording()
                print("Recording saved to", filename)
            break
        else:
            print("Invalid command. Please enter 'r' to start recording, 's' to stop recording, or 'q' to quit.")
        time.sleep(0.1)  # sleep to avoid high CPU usage in the loop

跟sphinx一样。只支持linux系统。
https://manual.manticoresearch.com/Server_settings/Special_suffixes
可以支持简单的PHP脚本

#!/usr/bin/php
...
<?php for ($i=1; $i<=6; $i++) { ?>
table test_<?=$i?> {
  type = rt
  path = /var/lib/manticore/data/test_<?=$i?>
  rt_field = subject
  ...
 }
 <?php } ?>
 ...

 <?php
 $confd_folder='/etc/manticore.conf.d/';
 $files = scandir($confd_folder);
 foreach($files as $file)
 {
         if(($file == '.') || ($file =='..'))
         {} else {
                 $fp = new SplFileInfo($confd_folder.$file);
                 if('conf' == $fp->getExtension()){
                         include ($confd_folder.$file);
                 }
         }
 }
 ?>