客户端套接字无法准确接收服务器端套接字发送的内容

时间:2016-08-27 16:30:26

标签: java android sockets

我一直在开发一款Android音频聊天程序,其行为类似于对讲机。在用户按下通话按钮之后,录音机开始记录用户说的内容并通过套接字将音频字节写入远程服务器。在服务器端,服务器套接字只是将收到的音频字节发送到其他客户端套接字。

我没有很好的方法来控制这些套接字的行为。例如,识别客户端套接字属于哪个用户?套接字没有任何字段来携带除写入数据之外的其他信息。所以最后,我得出的解决方案是使用相同的套接字传输音频数据以传输类似用户名字符串的内容。并且这很好,因为在客户端套接字成功创建与服务器套接字的连接的情况下,android客户端发出用户名字符串。

当我尝试发送用户名字符串以通知用户按下通话按钮时正在通话的其他客户端时,会发生灾难。让我举个例子来说明这一点:

  1. 用户的姓名是" user1"按下谈话按钮进行交谈。
  2. 应用程序发送字符串" usr:user1"到服务器端。
  3. 然后开始发送录音机生成的音频数据。
  4. 在服务器端,服务器收到了确切的" user1"和以下音频数据并重新发送到其他连接的客户端。但问题是客户似乎没有收到" usr:user1"所有的时间。

    以下是检查收到数据的方法:

     is = socket.getInputStream();
     byte[] buffer = new byte[minBufSize];
     numOfReceived = is.read(buffer);
     if(numOfReceived!=-1&&numOfReceived!=minBufSize){
         byte[] ub = new byte[numOfReceived];
         for(int i=0;i<numOfReceived;i++){
             ub[i]=buffer[i];
         }
         String usersString = new String(ub, "UTF-8");
         if(usersString.contains("hj:")){
             System.out.println("current:");
             final String userOfTalking=usersString.substring(3,usersString.length());
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     whoIsTalking.setText(userOfTalking+" is talking");
                     whoIsTalking.setVisibility(View.VISIBLE);
                 }
             });
             continue;
         }
    

    实际上,我不知道输入流是否包含音频数据或字符串数​​据。所以我尝试使用inputstream.read()的返回来找出输入流读取的字节数:

    • 如果返回的数字不等于-1(套接字关闭)或buffersize,我在outputstream.write中设置,然后我假设它是一个字符串。

    但这非常不可靠。例如,如果我循环命令socket.getoutstream.write(buffer,0,100),那么我应该从输入流中读取缓冲区100长度。但它不是这样的。我经常得到长度为60或40或任何小于100的数字的缓冲区。

    就像它声明的那样,输出流不会发送正好100个字节的数据。所以我的字符串数据只是混合了以下音频数据。因此,当应用程序在仅连接到服务器时发送用户名时,其他客户端将收到正确的字符串,因为没有后续音频数据来干扰它。

    你能告诉我一些你的意见吗?我猜对了吗?我怎么解决这个问题?当用户按下通话按钮时,我设法在应用程序发送用户名字符串后调用Thread.sleep(300),以便在它们混合的情况下在发送音频数据之间留出一些空间。但它不起作用。非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

如果我已经正确地阅读了这个...你发送的确是100个字节,但是副词read没有得到100,它会减少吗?

这可能有很多原因。一个是你在写作时没有打电话给flush()。如果是这种情况,那么您就会遇到错误,并且需要在发送代码中进行适当的flush()调用。

另外可能是因为操作系统正在分割数据包之间的数据。这对于小数据包(100字节)不太可能,但对于大数据包很可能/必要......

您不应该依赖于所有数据在一次读取中出现...您需要多次读取以汇总所有数据。

答案 1 :(得分:0)

自从我提出这个问题以来已经有一段时间了,我现在要给出自己的答案。希望它不会太晚。

实际上@Philip Couling在他的回答中提供了一些非常有价值的见解,它帮助我证实了我对这个问题的原因的猜测 - &#34; OS正在分割数据包之间的数据&#34;。再次感谢他的贡献。

解决此问题的方法来自我的一位朋友。他告诉我,我可以在客户端创建一个新套接字连接到同一服务器套接字,以字符串格式传输一些控制信息,告诉服务器谁开始说话,谁停止说话,甚至允许人们聊天。每个套接字都会向服务器发送一个字符串,以&#34;音频流:用户名&#34;等格式告诉他们正在做什么以及他们属于谁。或&#34;控制信息:用户名&#34;。并且服务器将它们分别存储在两个arraylist或hashmap中。因此,每当用户按下按钮以流式传输音频时,相应的控制信息字符串将被发送到服务器以告知其流来自谁,然后服务器通过套接字将该信息重定向到其他客户端以进行控制。所以现在我们将字符串数据传输到除传输音频流之外的专用套接字中。结果,&#34; Os分割数据&#34;不再是问题,因为字符串数据太短而不能触发操作系统对它们进行分段,也因为我们只是在特定事件上发送它们,而不是像发送音频流那样连续发送。

但是新的插座也带来了副作用。由于网络延迟,人们可能会发现他们在应用程序告诉他们有人停止说话后仍然会收到一段时间的声音。在极端网络条件下延迟可能超过10秒,如果有人在手机播放接收语音时开始说话,可能会导致强烈噪音。

为解决此问题,在音频插槽中传输字符串通知可能是保持每一方同步的唯一选择。但我想我们可以在音频数据和字符串数据之间插入一些空字节,以确保字符串不会与其他数据混合。(空字节不应该改变字符串。)但是我还没有尝试过这种方法。我会在检查后添加结果。