You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在两台Raspberry Pi3间不使用Socket,通过Java实现语音收发?

嘿,这个需求挺有意思的——不用Socket直接编码,在两台Raspberry Pi3之间用Java实现语音收发,咱们可以试试几个封装好的方案,完全不用自己写Socket逻辑,下面给你拆解几个实用的:

方案1:基于MQTT消息队列(最省心的网络方案)

MQTT是轻量级的消息协议,现成的客户端库已经把底层的Socket通信封装得明明白白,你只需要关注语音的录制、发送和接收播放就行。

准备工作

  1. 搭建MQTT Broker:在其中一台Pi或者你手头的其他设备上装Mosquitto(树莓派直接敲命令):

    sudo apt update && sudo apt install mosquitto mosquitto-clients
    

    装完后Broker默认启动,两台Pi能ping通的话就能用。

  2. 引入Java依赖:如果用Maven,在pom.xml里加Paho客户端依赖:

    <dependency>
        <groupId>org.eclipse.paho</groupId>
        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
        <version>1.2.5</version>
    </dependency>
    

发送端代码(录制并发布语音)

这个代码会录制10秒语音,转成字节数组后发布到pi/voice主题:

import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import javax.sound.sampled.*;
import java.io.ByteArrayOutputStream;

public class VoiceSender {
    private static final String BROKER_URL = "tcp://[你的BrokerIP]:1883";
    private static final String TOPIC = "pi/voice";
    private static final int RECORD_SECONDS = 10;
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        // 初始化MQTT客户端
        MqttClient client = new MqttClient(BROKER_URL, MqttClient.generateClientId());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        client.connect(options);

        // 录制语音
        TargetDataLine line = AudioSystem.getTargetDataLine(FORMAT);
        line.open(FORMAT);
        line.start();
        System.out.println("开始录制...");

        byte[] buffer = new byte[line.getBufferSize() / 5];
        int bytesRead;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        long endTime = System.currentTimeMillis() + RECORD_SECONDS * 1000;

        while (System.currentTimeMillis() < endTime) {
            bytesRead = line.read(buffer, 0, buffer.length);
            out.write(buffer, 0, bytesRead);
        }

        line.stop();
        line.close();
        System.out.println("录制结束,发送语音...");

        // 发布消息
        MqttMessage message = new MqttMessage(out.toByteArray());
        message.setQos(1); // 确保消息到达
        client.publish(TOPIC, message);

        client.disconnect();
    }
}

接收端代码(订阅并播放语音)

订阅pi/voice主题,收到字节流后直接播放:

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import javax.sound.sampled.*;

public class VoiceReceiver {
    private static final String BROKER_URL = "tcp://[你的BrokerIP]:1883";
    private static final String TOPIC = "pi/voice";
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        MqttClient client = new MqttClient(BROKER_URL, "VoiceReceiver", new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        client.connect(options);

        // 订阅主题并设置回调
        client.subscribe(TOPIC, (topic, message) -> {
            try {
                System.out.println("收到语音,开始播放...");
                // 播放语音
                SourceDataLine line = AudioSystem.getSourceDataLine(FORMAT);
                line.open(FORMAT);
                line.start();
                line.write(message.getPayload(), 0, message.getPayload().length);
                line.drain();
                line.stop();
                line.close();
                System.out.println("播放结束");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        // 保持程序运行
        Thread.sleep(Long.MAX_VALUE);
    }
}
方案2:基于共享目录(无网络依赖,适合近距离)

如果两台Pi在同一个局域网,也可以用SMB共享目录:发送端把录制好的语音文件写到共享目录,接收端监听目录变化,有新文件就播放。

准备工作

  1. 在Pi1上搭建SMB共享:

    • 安装Samba:sudo apt install samba
    • 编辑配置文件:sudo nano /etc/samba/smb.conf,在末尾加:
      [pi_shared]
         path = /home/pi/voice_shared
         writeable = yes
         guest ok = no
         create mask = 0644
         directory mask = 0755
      
    • 创建共享目录:mkdir /home/pi/voice_shared
    • 设置Samba用户密码:sudo smbpasswd -a pi
    • 重启Samba服务:sudo systemctl restart smbd
  2. 在Pi2上挂载共享目录:

    sudo mount -t cifs //[Pi1_IP]/pi_shared /mnt/voice_shared -o username=pi,password=你的密码
    

    想要开机自动挂载,把这条命令加到/etc/fstab里就行。

发送端代码(录制到共享目录)

import javax.sound.sampled.*;
import java.io.File;
import java.io.FileOutputStream;

public class FileVoiceSender {
    private static final String SHARED_DIR = "/mnt/voice_shared";
    private static final int RECORD_SECONDS = 10;
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        TargetDataLine line = AudioSystem.getTargetDataLine(FORMAT);
        line.open(FORMAT);
        line.start();
        System.out.println("开始录制...");

        byte[] buffer = new byte[line.getBufferSize() / 5];
        int bytesRead;
        File outputFile = new File(SHARED_DIR, "voice_" + System.currentTimeMillis() + ".pcm");
        FileOutputStream out = new FileOutputStream(outputFile);

        long endTime = System.currentTimeMillis() + RECORD_SECONDS * 1000;
        while (System.currentTimeMillis() < endTime) {
            bytesRead = line.read(buffer, 0, buffer.length);
            out.write(buffer, 0, bytesRead);
        }

        line.stop();
        line.close();
        out.close();
        System.out.println("录制完成,文件已保存到共享目录");
    }
}

接收端代码(监听目录并播放)

用Java的WatchService监听目录变化:

import javax.sound.sampled.*;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.*;

public class FileVoiceReceiver {
    private static final String SHARED_DIR = "/mnt/voice_shared";
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        Path dir = Paths.get(SHARED_DIR);
        WatchService watchService = FileSystems.getDefault().newWatchService();
        dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);

        System.out.println("监听共享目录中...");
        while (true) {
            WatchKey key = watchService.take();
            for (WatchEvent<?> event : key.pollEvents()) {
                if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
                    File voiceFile = dir.resolve((Path) event.context()).toFile();
                    // 等文件写完再读取
                    Thread.sleep(1000);
                    System.out.println("发现新文件:" + voiceFile.getName() + ",开始播放");

                    // 播放文件
                    FileInputStream in = new FileInputStream(voiceFile);
                    SourceDataLine line = AudioSystem.getSourceDataLine(FORMAT);
                    line.open(FORMAT);
                    line.start();

                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        line.write(buffer, 0, bytesRead);
                    }

                    line.drain();
                    line.stop();
                    line.close();
                    in.close();
                    System.out.println("播放结束");
                }
            }
            key.reset();
        }
    }
}
方案3:基于蓝牙RFCOMM(无网络,适合近距离)

Pi3自带蓝牙模块,用RFCOMM协议可以模拟串口通信,Java用BlueCove库就能实现,完全不用自己写Socket代码。

准备工作

  1. 开启并配对两台Pi的蓝牙:
    • 开启蓝牙:sudo bluetoothctl power on
    • 用bluetoothctl搜索并配对对方的Pi,记得信任设备:trust [对方蓝牙MAC]
  2. 引入BlueCove依赖:Maven依赖如下(注意树莓派用的是ARM架构,可能需要对应版本):
    <dependency>
        <groupId>com.intel.bluetooth</groupId>
        <artifactId>bluecove</artifactId>
        <version>2.1.1</version>
    </dependency>
    <dependency>
        <groupId>com.intel.bluetooth</groupId>
        <artifactId>bluecove-gpl</artifactId>
        <version>2.1.1</version>
    </dependency>
    

蓝牙服务端代码(接收语音)

import javax.bluetooth.*;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
import javax.sound.sampled.*;
import java.io.InputStream;

public class BluetoothVoiceServer {
    private static final UUID UUID = new UUID("0000110100001000800000805F9B34FB", false);
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        // 初始化蓝牙服务
        LocalDevice localDevice = LocalDevice.getLocalDevice();
        localDevice.setDiscoverable(DiscoveryAgent.GIAC);

        String url = "btspp://localhost:" + UUID + ";name=VoiceServer";
        StreamConnectionNotifier notifier = (StreamConnectionNotifier) Connector.open(url);
        System.out.println("蓝牙服务已启动,等待连接...");

        StreamConnection connection = notifier.acceptAndOpen();
        System.out.println("客户端已连接");

        // 接收并播放语音
        InputStream in = connection.openInputStream();
        SourceDataLine line = AudioSystem.getSourceDataLine(FORMAT);
        line.open(FORMAT);
        line.start();

        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            line.write(buffer, 0, bytesRead);
        }

        line.drain();
        line.stop();
        line.close();
        in.close();
        connection.close();
        notifier.close();
    }
}

蓝牙客户端代码(发送语音)

import javax.bluetooth.RemoteDevice;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.sound.sampled.*;
import java.io.OutputStream;

public class BluetoothVoiceClient {
    private static final UUID UUID = new UUID("0000110100001000800000805F9B34FB", false);
    private static final String REMOTE_MAC = "[对方Pi的蓝牙MAC地址]";
    private static final int RECORD_SECONDS = 10;
    private static final AudioFormat FORMAT = new AudioFormat(44100, 16, 1, true, true);

    public static void main(String[] args) throws Exception {
        // 连接蓝牙设备
        RemoteDevice remoteDevice = RemoteDevice.getRemoteDevice(REMOTE_MAC);
        String url = "btspp://" + REMOTE_MAC + ":" + UUID + ";authenticate=false;encrypt=false;master=false";
        StreamConnection connection = (StreamConnection) Connector.open(url);
        System.out.println("已连接到蓝牙服务端");

        // 录制并发送语音
        TargetDataLine line = AudioSystem.getTargetDataLine(FORMAT);
        line.open(FORMAT);
        line.start();
        System.out.println("开始录制...");

        OutputStream out = connection.openOutputStream();
        byte[] buffer = new byte[line.getBufferSize() / 5];
        int bytesRead;
        long endTime = System.currentTimeMillis() + RECORD_SECONDS * 1000;

        while (System.currentTimeMillis() < endTime) {
            bytesRead = line.read(buffer, 0, buffer.length);
            out.write(buffer, 0, bytesRead);
        }

        line.stop();
        line.close();
        out.close();
        connection.close();
        System.out.println("发送完成");
    }
}
注意事项
  • 所有方案里的音频格式要统一,比如示例里用的是44100Hz、16bit、单声道、PCM编码,不然播放会出现杂音或者无法播放的情况。
  • 树莓派上运行Java程序时,要确保有音频权限:可以用sudo usermod -a -G audio pi把pi用户加到audio组。
  • 蓝牙方案如果遇到连接问题,检查两台Pi的蓝牙是否配对成功,是否信任了对方设备。
  • 共享目录方案要确保挂载稳定,避免出现读写权限问题。

内容的提问来源于stack exchange,提问作者mbcode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:14:35