<think>我们之前已经对Android端和Python端进行了多次修改,但问题仍然存在。现在我们需要更深入地排查问题。考虑到问题描述为“脚本还是收不到”,我们需要从网络通信、数据格式、线程同步等多个方面进行彻底检查。解决方案思路:1.确保ADB端口转发正确建立。2.确保Android端和Python端的Socket连接已经成功建立。3.确保Android端发送的数据格式与Python端解析格式完全匹配。4.添加详细的日志,以便于定位问题。5.考虑使用网络抓包工具(如Wireshark)检查数据是否真正从Android发出(但ADB转发在本地,可能抓不到,所以主要靠日志)。6.在Python端增加超时和重连机制。我们将对Android端和Python端代码进行以下关键修改:Android端修改:1.在发送数据包之前,记录发送的数据包大小和内容摘要(注意不要记录整个音频数据,避免日志过多)。2.确保在发送数据时,Socket的输出流是有效的,并且每次发送后都进行了flush。3.增加心跳机制,定期发送ping包,确保连接活跃。
Python端修改:1.增加接收数据的日志,记录每次接收到的数据长度和开头部分。2.修改数据包解析逻辑,确保能够正确分割数据包(使用双换行符作为分隔符)。3.增加重连机制,当连接断开时自动重连。由于之前已经提供了较完整的代码,这里我们只提供关键修改部分的代码。注意:由于问题可能出现在多个环节,我们将分别对Android和Python代码进行增强。一、Android端关键修改:1.在发送音频数据包时,增加日志记录(注意:音频数据较大,我们只记录发送的包计数和大小):```javaprivatevoid uploadAudioData() {//...之前的代码...try {intbytesRead= audioRecord.read(buffer,0, BUFFER_SIZE);if (bytesRead>0){//创建JSON数据包JSONObjectjson =new JSONObject();json.put("type","recording");Stringbase64Data =Base64.encodeToString(buffer,0, bytesRead,Base64.NO_WRAP);json.put("data",base64Data);//发送数据synchronized(this) {if(socketWriter !=null){socketWriter.write(json.toString());socketWriter.write("\n\n");//添加双换行作为结束标识socketWriter.flush();Log.d(TAG,"成功发送音频数据包,大小: "+ (json.toString().length())+ "字符");}}//...发送消息 ...}}catch (Exception e) {Log.e(TAG,"发送音频数据失败",e);sendErrorMessage("发送音频数据失败: "+ e.getMessage());}}```2.增加心跳机制,定期发送ping包(在连接建立后开始,断开后停止):```java//在MainActivity中增加privateScheduledExecutorService heartbeatScheduler;//在连接成功时启动心跳privatevoid startHeartbeat() {if(heartbeatScheduler !=null){heartbeatScheduler.shutdownNow();}heartbeatScheduler =Executors.newSingleThreadScheduledExecutor();heartbeatScheduler.scheduleAtFixedRate(() ->{if (isServerRunning &&clientSocket!= null&& !clientSocket.isClosed()){sendControlPacket("ping");}},1,5,TimeUnit.SECONDS);//每5秒发送一次}//在关闭连接时停止心跳privatevoid stopHeartbeat() {if(heartbeatScheduler !=null){heartbeatScheduler.shutdownNow();heartbeatScheduler =null;}}//在startServer方法中,当客户端连接成功时调用startHeartbeat()//在closeSocket()方法中调用stopHeartbeat()```3.在连接建立和断开时,增加日志记录。二、Python端关键修改:1.增强接收数据的日志,记录接收到的字节数以及开头部分(为了避免日志过大,只记录开头少量字节):```pythonclass AudioReceiver(threading.Thread):defrun(self):# ...之前的代码 ...whileself.running:try:data =self.sock.recv(8192)ifnot data:logger.warning("连接已断开")breaklogger.debug(f"收到数据:{len(data)}字节,前20字节:{data[:20]}")#...处理数据...```2.确保数据包分割逻辑正确,我们使用双换行符`\n\n`作为分隔符。注意:由于数据是二进制流,我们使用`b'\n\n'`来查找。3.修改数据包处理逻辑,确保能够正确解析JSON:```pythondefprocess_buffer(self):whileTrue:idx= self.buffer.find(b'\n\n')ifidx ==-1:breakpacket_bytes =self.buffer[:idx]self.buffer= self.buffer[idx+2:]#移除已处理的数据包和分隔符#尝试解析为JSONtry:packet_str =packet_bytes.decode('utf-8')try:json_data= json.loads(packet_str)#根据类型放入不同队列if json_data.get('type') =='recording':audio_packet_queue.put(packet_bytes)#注意:这里放入的是原始字节,后续处理需要再次解析logger.info(f"收到音频数据包,大小: {len(packet_bytes)}字节")else:control_queue.put(json_data)logger.info(f"收到控制指令:{json_data}")exceptjson.JSONDecodeError:logger.warning(f"JSON解析失败,数据包内容:{packet_str[:200]}...")except UnicodeDecodeError:logger.warning(f"收到非UTF-8数据,大小: {len(packet_bytes)}字节")```4.在音频处理线程中,对音频包进行解析:```pythonclass AudioProcessorThread(threading.Thread):def run(self):whileself.running:try:#从audio_packet_queue获取数据包(原始字节)audio_packet =audio_packet_queue.get(timeout=0.5)#解析音频包packet_str =audio_packet.decode('utf-8')packet =json.loads(packet_str)if packet['type'] !='recording':continueaudio_data =base64.b64decode(packet['data'])#后续处理...exceptqueue.Empty:pass```5.增加重连机制(之前已有,但需要确保其可靠性)。由于代码较长,这里只给出关键修改部分。如果问题仍然存在,请运行修改后的代码并收集日志(Android的logcat和Python的日志),以便进一步分析。如果上述修改后仍然无法解决问题,我们可以尝试以下额外步骤:1.在Android端,尝试发送一个简单的字符串(如"test")到Python端,看是否能收到。2.在Python端,使用简单的echo服务器测试Android端是否能发送成功(排除Python端的问题)。但是,为了节省时间,我们先按照上述修改进行。由于我们之前已经提供了完整的代码,这里只提供修改的片段。如果需要完整代码,请参考之前的回复并整合这些修改。最后,请确保在AndroidManifest.xml中已经声明了网络权限:```xml<uses-permissionandroid:name="android.permission.INTERNET" />```注意:在Android9及以上版本,默认不允许明文传输,如果使用localhost通信则不受影响,但如果使用实际IP,则需要配置网络安全。由于我们使用ADB转发,所以是localhost通信,不需要额外配置。如果问题仍然存在,请提供最新的日志(Android端和Python端)。由于问题描述有限,以上方案是基于常见问题的通用解决方案。以下是完整的Python端修改后的代码(包含上述关键修改):</think>我理解您的问题:即使经过多次修改,Python脚本仍然无法接收到Android发送的音频数据。这是一个复杂的网络通信问题,需要深入排查。我将提供一套完整的诊断和解决方案,包括增强的日志系统、连接验证工具和备选通信方法。
### 终极解决方案:三步诊断法
#### 第一步:Android端增强日志系统
```java
// 在MainActivity中添加详细的网络诊断日志
private void logNetworkStatus() {
new Thread(() -> {
while (isServerRunning) {
try {
String status = "网络状态: ";
if (clientSocket == null) {
status += "未连接";
} else {
status += "已连接 - ";
status += "本地端口: " + clientSocket.getLocalPort() + ", ";
status += "远程地址: " + clientSocket.getInetAddress().getHostAddress() + ", ";
status += "远程端口: " + clientSocket.getPort() + ", ";
status += "是否关闭: " + clientSocket.isClosed();
}
Log.d(TAG, status);
sendNetworkMessage(status);
// 发送测试数据包
if (clientSocket != null && !clientSocket.isClosed()) {
sendTestPacket();
}
Thread.sleep(5000); // 每5秒记录一次
} catch (Exception e) {
Log.e(TAG, "网络状态监控错误", e);
}
}
}).start();
}
private void sendTestPacket() {
try {
JSONObject testPacket = new JSONObject();
testPacket.put("type", "test");
testPacket.put("timestamp", System.currentTimeMillis());
testPacket.put("message", "Android->PC测试数据包");
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(testPacket.toString());
socketWriter.write("\n\n");
socketWriter.flush();
Log.i(TAG, "测试数据包已发送");
}
}
} catch (Exception e) {
Log.e(TAG, "发送测试数据包失败", e);
}
}
// 在onCreate方法中启动
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...其他代码...
startServer(30000);
logNetworkStatus(); // 添加这一行
}
```
#### 第二步:Python端增强诊断脚本
```python
import socket
import time
import json
import logging
import threading
# 配置详细日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("audio_receiver_debug.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger("AudioDebug")
class NetworkDiagnostics:
def __init__(self, host='localhost', port=35000):
self.host = host
self.port = port
self.sock = None
self.running = True
self.last_packet_time = time.time()
self.packet_count = 0
self.connection_status = "disconnected"
def connect(self):
"""尝试连接到ADB转发的端口"""
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(5)
self.sock.connect((self.host, self.port))
self.connection_status = "connected"
logger.info(f"成功连接到 {self.host}:{self.port}")
return True
except Exception as e:
self.connection_status = f"连接失败: {str(e)}"
logger.error(f"连接失败: {e}")
return False
def monitor_traffic(self):
"""监控网络流量并记录统计信息"""
buffer = bytearray()
while self.running:
try:
# 接收数据
data = self.sock.recv(4096)
if not data:
logger.warning("连接已断开")
self.connection_status = "disconnected"
break
self.last_packet_time = time.time()
buffer.extend(data)
# 处理数据包
packets = self.extract_packets(buffer)
for packet in packets:
self.process_packet(packet)
# 记录状态
status = self.get_network_status()
logger.info(status)
except socket.timeout:
# 检查是否超时
if time.time() - self.last_packet_time > 10:
logger.warning("10秒未收到数据,连接可能已断开")
self.connection_status = "timeout"
except Exception as e:
logger.error(f"接收数据错误: {e}")
self.connection_status = f"error: {str(e)}"
def extract_packets(self, buffer):
"""从缓冲区提取完整的数据包"""
packets = []
while True:
# 查找双换行符作为包结束符
idx = buffer.find(b'\n\n')
if idx == -1:
break
packet_bytes = buffer[:idx]
del buffer[:idx+2]
try:
# 尝试解析为JSON
packet_str = packet_bytes.decode('utf-8')
packets.append(packet_str)
except UnicodeDecodeError:
logger.warning(f"收到非文本数据: {packet_bytes[:50]}...")
return packets
def process_packet(self, packet_str):
"""处理接收到的数据包"""
try:
packet = json.loads(packet_str)
self.packet_count += 1
if packet.get('type') == 'test':
logger.info(f"收到测试数据包: {packet}")
elif packet.get('type') == 'recording':
logger.debug(f"收到音频数据包 #{self.packet_count}")
else:
logger.info(f"收到未知数据包: {packet}")
except json.JSONDecodeError:
logger.warning(f"无效JSON数据: {packet_str[:100]}...")
def get_network_status(self):
"""获取当前网络状态"""
status = {
"connection": self.connection_status,
"packets_received": self.packet_count,
"last_packet": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.last_packet_time)),
"uptime": f"{time.time() - self.start_time:.1f}秒"
}
return json.dumps(status, indent=2)
def start(self):
"""启动诊断工具"""
self.start_time = time.time()
if self.connect():
threading.Thread(target=self.monitor_traffic, daemon=True).start()
def stop(self):
"""停止诊断工具"""
self.running = False
if self.sock:
self.sock.close()
if __name__ == '__main__':
# 使用示例
diag = NetworkDiagnostics()
diag.start()
try:
# 保持主线程运行
while True:
time.sleep(1)
except KeyboardInterrupt:
diag.stop()
logger.info("诊断工具已停止")
```
#### 第三步:备选通信方案(WebSocket)
如果TCP套接字仍然无法工作,改用WebSocket协议:
1. Android端添加WebSocket支持
```gradle
// 在app/build.gradle中添加依赖
dependencies {
implementation 'org.java-websocket:Java-WebSocket:1.5.3'
}
```
```java
// WebSocket客户端实现
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
public class WebSocketManager {
private static final String TAG = "WebSocket";
private WebSocketClient webSocketClient;
private Context context;
public WebSocketManager(Context context) {
this.context = context;
}
public void connect(String serverUrl) {
try {
URI uri = new URI(serverUrl);
webSocketClient = new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake handshakedata) {
Log.i(TAG, "WebSocket连接已打开");
}
@Override
public void onMessage(String message) {
Log.d(TAG, "收到消息: " + message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
Log.i(TAG, "连接关闭: " + reason);
}
@Override
public void onError(Exception ex) {
Log.e(TAG, "WebSocket错误", ex);
}
};
webSocketClient.connect();
} catch (Exception e) {
Log.e(TAG, "创建WebSocket失败", e);
}
}
public void sendAudioData(byte[] audioData) {
if (webSocketClient != null && webSocketClient.isOpen()) {
String base64Data = Base64.encodeToString(audioData, Base64.NO_WRAP);
JSONObject json = new JSONObject();
try {
json.put("type", "audio");
json.put("data", base64Data);
webSocketClient.send(json.toString());
} catch (JSONException e) {
Log.e(TAG, "创建JSON失败", e);
}
}
}
public void disconnect() {
if (webSocketClient != null) {
webSocketClient.close();
}
}
}
```
2. Python端WebSocket服务器
```python
import asyncio
import websockets
import json
import base64
import logging
logging.basicConfig(level=logging.INFO)
async def audio_server(websocket, path):
logging.info("客户端已连接")
try:
async for message in websocket:
try:
data = json.loads(message)
if data.get('type') == 'audio':
audio_data = base64.b64decode(data['data'])
logging.info(f"收到音频数据: {len(audio_data)}字节")
# 处理音频数据...
except json.JSONDecodeError:
logging.warning("收到非JSON消息")
except websockets.exceptions.ConnectionClosed:
logging.info("客户端断开连接")
start_server = websockets.serve(audio_server, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
```
### 系统化诊断流程
1. **验证ADB端口转发**
```bash
adb forward --list
# 应该显示: tcp:35000 tcp:30000
# 测试端口是否开放
telnet localhost 35000
```
2. **检查Android网络状态**
- 查看Android日志中的网络状态信息
- 确认测试数据包是否发送成功
3. **运行Python诊断工具**
```bash
python network_diagnostic.py
```
- 检查日志文件`audio_receiver_debug.log`
- 查看是否收到测试数据包
4. **网络抓包分析**
```bash
# 在PC端捕获流量
tcpdump -i lo -s0 -w android_capture.pcap port 35000
# 在Android端捕获流量(需要root)
tcpdump -i any -s0 -w /sdcard/capture.pcap port 30000
```
5. **防火墙验证**
```bash
# 检查PC防火墙
sudo ufw status
# 检查Android防火墙(通常无)
```
### 常见问题终极解决方案
1. **如果ADB转发失败**
```bash
# 移除所有转发
adb forward --remove-all
# 重启ADB服务
adb kill-server
adb start-server
# 重新建立转发
adb forward tcp:35000 tcp:30000
```
2. **如果设备存在多个网络接口**
```java
// 在Android端指定IP地址
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("192.168.1.100", 30000));
```
3. **如果存在协议不匹配**
```java
// 在Android端添加协议头
String header = "AUDIO/v1.0\n";
socketWriter.write(header);
```
4. **如果存在字节顺序问题**
```python
# 在Python端处理字节顺序
import struct
audio_data = struct.unpack('<h', packet)[0] # 小端序
```
### 最终验证步骤
1. 在Android和PC上同时运行诊断工具
2. 检查Android是否发送测试数据包
3. 检查PC是否收到测试数据包
4. 如果测试包成功,再尝试发送音频数据
5. 如果仍然失败,切换到WebSocket方案
这些工具和方法应该能彻底解决"脚本收不到数据"的问题。如果经过所有这些步骤问题仍然存在,可能需要考虑硬件或操作系统级别的网络问题。