通过 TCP 套接字将音频写入服务器
Posted
技术标签:
【中文标题】通过 TCP 套接字将音频写入服务器【英文标题】:Writing audio to server over TCP socket 【发布时间】:2016-04-24 18:52:30 【问题描述】:我正在尝试通过 TCP 套接字将实时麦克风录音传输到服务器,然后服务器将输入流写入文件。 连接已建立,但一段时间后,我的客户端出现连接被拒绝错误。
服务器代码:
public class auServer extends Thread
private static ServerSocket serverSocket;
private static int port = 3333;
public void run()
System.out.println("init success");
while(true)
try
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
Socket clientSoc = serverSocket.accept();
System.out.println("Waiting for client on port " +serverSocket.getLocalPort() + "...");
System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());
InputStream in = clientSoc.getInputStream();
while(in!=null)
writeToFile(in);
System.out.println("socket");
clientSoc.close();
catch(SocketTimeoutException s)
System.out.println("Socket timed out!");
break;
catch(IOException e)
e.printStackTrace();
System.out.println("some io");
break;
catch (Exception e)
System.out.println("some e");
e.printStackTrace();
private void writeToFile(InputStream in) throws IOException
// Write the output audio in byte
String filePath = "8k16bitMono1.wav";
short sData[] = new short[1024];
byte[] bData = IOUtils.toByteArray(in);
FileOutputStream os = null;
try
os = new FileOutputStream(filePath);
catch (FileNotFoundException e)
e.printStackTrace();
System.out.println("Short wirting to file" + sData.toString());
try
os.write(bData, 0, 2048);
catch (IOException e)
e.printStackTrace();
try
os.close();
catch (IOException e)
e.printStackTrace();
public static void main(String[] args)
// TODO Auto-generated method stub
try
Thread serverThread = new auServer();
serverThread.run();
System.out.println("runing");
catch(IOException e)
e.printStackTrace();
和客户:
private void streamData(byte[] bData) throws UnknownHostException, IOException, InterruptedException //bData is byte array to transmit
Thread.sleep(500);
Socket client = new Socket("10.221.40.41",3333);
OutputStream outToServer = client.getOutputStream();
outToServer.write(bData);
if(!isRecording)
client.close();
可能是什么问题? 提前致谢。
【问题讨论】:
有什么例外吗?请阅读一些“阻塞网络 I/O 教程”。代码存在多个问题。 “试图传输实时麦克风录音”——你对“实时”的定义是什么? 'streamData(byte[] bData)' bData 有多少字节?您是在说一次 streamData() 调用吗? 为什么要为每条数据创建一个新的客户端?为什么服务器一次又一次地将这些片段写入同一个文件?该文件不会增长,但只包含一份。请说明您的想法。os.write(bData, 0, 2048);
。 2048 年?你确定吗?为什么?
【参考方案1】:
我会逐条评论你的代码。
private static ServerSocket serverSocket;
这没有理由是静态的。
while(true)
try
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
最后两行应该在循环之前。这就是连接被拒绝的原因,也会导致你没有提到的BindExceptions
。不清楚为什么需要超时。
Socket clientSoc = serverSocket.accept();
System.out.println("Waiting for client on port " +serverSocket.getLocalPort() + "...");
不,你不是。他已经联系上了。你在accept().
System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());
InputStream in = clientSoc.getInputStream();
while(in!=null)
循环和测试都是徒劳的。该变量最初不是空的,而且它永远不可能变为空。循环是徒劳的,因为writeToFile()
方法完全耗尽了输入流,因此再也没有什么可读取的了。这将导致您没有提到的垃圾数据。
writeToFile(in);
System.out.println("socket");
毫无意义的消息。
clientSoc.close();
accept()
之后的行到这里的所有代码都应该在单独的线程中执行。接受循环应该只接受连接并启动线程。
catch(SocketTimeoutException s)
System.out.println("Socket timed out!");
这里超时的是accept()
,因为监听套接字是您设置超时的唯一套接字。我怀疑你需要这个。
break;
catch(IOException e)
e.printStackTrace();
System.out.println("some io");
另一个无用的消息。
break;
catch (Exception e)
System.out.println("some e");
又一个。当您遇到异常时,打印异常。这不是您自己设计的无用信息。否则调试就变成了一场猜谜游戏。
e.printStackTrace();
private void writeToFile(InputStream in) throws IOException
// Write the output audio in byte
String filePath = "8k16bitMono1.wav";
short sData[] = new short[1024];
未使用。删除。
byte[] bData = IOUtils.toByteArray(in);
不要使用这个。它浪费空间并增加延迟。请参阅下面的正确解决方案。
FileOutputStream os = null;
try
os = new FileOutputStream(filePath);
catch (FileNotFoundException e)
e.printStackTrace();
技术差。这个catch
放错地方了。取决于try
块中代码是否成功的代码应该在同一个try
块内。目前你正在经历这个try-catch
,好像从未发生过异常,这将导致下面的代码中出现NullPointerException
。
System.out.println("Short wirting to file" + sData.toString());
另一个毫无意义的消息。拼写错误; sData
里面什么都没有; sData.toString()
无论内容如何,都不会打印任何有用的东西;并且不正确,因为您根本没有写sData
。
try
os.write(bData, 0, 2048);
这会准确写入 2048 个字节,而不管读取的数量可能更少或更多。如果它小于它会抛出一个ArrayIndexOutOfBoundsException
或类似的,我希望在第二次调用中看到它,尽管你没有提到它,因为数组在第二次和后续调用中应该是零长度(如果有的话)。它应该是一个循环,一般形式:
int count;
byte[] buffer = new byte[8192]; // or more if you like
while ((count = in.read(buffer)) > 0)
out.write(buffer, 0, count);
回到你的代码:
catch (IOException e)
e.printStackTrace();
try
os.close();
catch (IOException e)
e.printStackTrace();
public static void main(String[] args)
// TODO Auto-generated method stub
try
Thread serverThread = new auServer();
serverThread.run();
这会运行线程的run()
方法。它不会启动线程。应该是serverThread.start().
System.out.println("runing");
拼写错误。在解决上述启动/运行问题之前,在服务器的 run()
方法退出之前,您不会看到此消息。
和客户:
private void streamData(byte[] bData) throws UnknownHostException, IOException, InterruptedException //bData is byte array to transmit
Thread.sleep(500);
毫无意义。消除。不要在网络代码中添加睡眠。
Socket client = new Socket("10.221.40.41",3333);
不要为每个缓冲区创建新连接。在客户端的整个生命周期中使用相同的连接。
OutputStream outToServer = client.getOutputStream();
outToServer.write(bData);
if(!isRecording)
client.close();
这应该是无条件的,并且应该在其他地方,以及套接字的创建。
可能是什么问题?
问题。复数。多种的。见上文。
【讨论】:
很好的解释,这正是我所期待的,REVIEW(+1)。感谢您的努力,将尝试使用上述指南,如果可行,将标记为答案。再次感谢,抱歉粗鲁:) @EJP 客户端的一件事,如果我没有为每个缓冲区建立新连接并在每个循环中关闭它,它会发送 10XXX~ 字节的完整记录(直到我遇到退出 while 的情况)同时。我想将它作为 2048 字节连续发送(流)而不是最后发送。 连续流式传输正是建议的更改所发生的情况。我不明白你所说的“不在最后”是什么意思。【参考方案2】:我想出了解决办法。 问题实际上是由于错误编码的流而阻塞了网络 I/O。
我错过的是
InputStream in = clientSoc.getInputStream();
DataInputStream DataIn =new DataInputStream(in)
while(DataIn!=null)
writeToFile(DataIn);
还有客户端
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.write(sData);
【讨论】:
不,您没有找到解决方案。while (DataIn != null)
开始没有意义了。无论是最初还是之后,它都不能为空,因此循环永远不会终止,并且尝试多次从同一个流中写入是徒劳的,或者应该是。在这种情况下,更改为 DataInputStream
和 DataOutputStream
也是徒劳的。您的代码有很多问题,但它们与这些完全不同。
@EJP 我成功地获取了直播数据包并在服务器端播放它们。 DataIn!=null
可能是错误的条件,但是当我添加 DataOutputStream out = new DataOutputStream(outToServer);
时,它可以正常工作。我同意代码可能有问题,但请帮助我解决问题,而不是批评并使其成为-1。
您一定也更改了其他内容。这种变化没有任何改变。 DataOutputStream.write()
和 DataInputStream.read()
与任何其他读取或写入方法没有什么不同。您在此处发布的代码会引发运行时异常。如果您不希望您的代码受到批评,那么发布它没有多大意义。如果它是完美的,你就不会有问题。你到处乱下结论。关于谁对您的答案投了反对票,您的证据为零。您似乎不了解此网站的工作原理,也不了解代码审查。
注意,如果您真的认为数据输入/输出流具有解决此问题所需的神奇属性,您需要说明原因,以及这些属性是什么。否则你的答案就是货物崇拜编程。【参考方案3】:
我猜你的客户端是一个安卓应用程序。有一次,我尝试通过套接字连接读取和写入数据到 android 应用程序。最终,我通过使用 adb(android 调试器桥)来做到这一点。
比方说,有一个 java 应用程序创建了一个端口号为 1992 的 Socket
。还有一个 android 应用程序创建了一个端口号为 1993 的 ServerSocket
。
真正的交易来了!
在开始运行应用程序之前,需要在 adb shell 中执行以下命令(或者您可以在 cmd/terminal 中找到您的 adb 并执行该命令)
adb forward tcp:1992 tcp:1993
转发端口后,您可以使用套接字的 IO 流从 java 应用程序向 android 应用程序写入和读取数据,反之亦然。
桌面应用代码sn-p:
while (flag)
try
final Socket socket = new Socket("localhost", 1992);
new Thread()
@Override
public void run()
while (flag)
try
new PrintWriter(socket.getOutputStream(), true).println(new Date().toString());
catch (IOException ex)
break;
.start();
catch (IOException ex)
// need port-forwarding
Android应用代码sn-p:
while (flag)
try
final Socket socket = new ServerSocket(1993).accept();
new AsyncTask<Void, Void, Void>()
@Override
public Void doInBackground(Void... params)
while (flag)
try
String line = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine();
Log.d("", line);
catch (IOException ex)
return null;
return null;
.execute();
catch (IOException ex)
break;
您可以将音频转换为字节数组并将其写入输出流。希望我的回答对你有用。
【讨论】:
对不起,我的问题没有很好地提出,我的 android 应用程序会将音频流式传输到服务器。在我的代码中,我只传输字节数据,我不明白为什么需要ADB forward tcp
。请解释。感谢您的努力。【参考方案4】:
serversocket 的创建必须在循环之外完成。 对于并行连接,您需要为每个连接启动一个线程。
还将超时应用于已建立的连接。 serverSocket 上的超时将在超时后结束接受,这在服务器中不是你想要的。
...
public void run()
ServerSocket serverSocket;
try
serverSocket = new ServerSocket(port);
catch (Exception ex)
ex.printStackTrace();
return;
System.out.println("init success");
while (true)
try
System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
final Socket clientSoc = serverSocket.accept();
clientSoc.setSoTimeout(10000);
System.out.println("Just connected to " + clientSoc.getRemoteSocketAddress());
new Thread()
public void run()
try
InputStream in = clientSoc.getInputStream();
writeToFile(in);
catch (Exception e)
e.printStackTrace();
finally
try
clientSoc.close();
catch (Exception ignore)
.start();
catch (Exception ex)
ex.printStackTrace();
...
【讨论】:
以上是关于通过 TCP 套接字将音频写入服务器的主要内容,如果未能解决你的问题,请参考以下文章
利用TCP服务器接收多客户端的音频数据,通过Naudio进行播放及音频处理TCP服务器