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

Java 客户端套接字无法准确接收服务器端套接字发送的内容,java,android,sockets,Java,Android,Sockets,我一直在开发一个Android音频聊天程序,它的行为就像一个对讲机。用户按下通话按钮后,录音机开始记录用户所说的内容,并通过套接字将音频字节写入远程服务器。在服务器端,服务器套接字只将接收到的音频字节发送到其他客户端套接字 我没有控制这些套接字行为的好方法。例如,要确定客户端套接字属于哪个用户?套接字没有任何字段来承载它写入的数据以外的附加信息。最后,我想出了一个解决方案,就是使用传输音频数据的同一个套接字来传输用户名字符串之类的内容。在客户端套接字成功创建到服务器套接字的连接的情况下,andr

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

我没有控制这些套接字行为的好方法。例如,要确定客户端套接字属于哪个用户?套接字没有任何字段来承载它写入的数据以外的附加信息。最后,我想出了一个解决方案,就是使用传输音频数据的同一个套接字来传输用户名字符串之类的内容。在客户端套接字成功创建到服务器套接字的连接的情况下,android客户端会发送一个用户名字符串

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

  • 名为“user1”的用户按talk按钮进行交谈
  • 应用程序将字符串“usr:user1”发送到服务器端
  • 然后,它开始发送由音频记录器生成的音频数据
  • 在服务器端,服务器接收到准确的“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;
         }
    
    is=socket.getInputStream();
    字节[]缓冲区=新字节[minBufSize];
    numOfReceived=is.read(缓冲区);
    if(numOfReceived!=-1&&numOfReceived!=minBufSize){
    byte[]ub=新字节[numOfReceived];
    
    对于(int i=0;i,如果我正确地读过它……您只发送了100个字节,但是后续的
    read
    没有得到100,它会得到更少

    这可能有很多原因。其中一个原因是您在编写时没有调用
    flush()
    。如果是这种情况,那么您有一个bug,需要在发送代码中放入一个适当的
    flush()
    调用

    另一种可能是因为操作系统正在对数据包之间的数据进行分段。这对于小数据包(100字节)来说不太可能,但对于大数据包来说非常可能/必要


    你永远不应该依赖于一次读取就能找到所有数据……你需要多次读取才能收集所有数据。

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

    事实上,@Philip Couling在他的回答中给出了一些非常有价值的见解,这帮助我确认了我对问题原因的猜测——“操作系统正在数据包之间分割数据”。再次感谢他的贡献

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

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


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

    哇,这是一堆文字。肯定太长了,没有读过。无论如何,你需要一个。@JonathonReinhart最小示例对于这个特定的问题不太可能可行。建设性的输入将包括一个副本编辑。我已经尽力了。@user1870797 I tr我尽力去编辑。如果可能的话,你认为你可以删除一些对澄清问题没有特别帮助的细节吗?这可能会让一些人更容易理解你的问题。你也可以清理你的接收器片段(它有一个杂散的
    continue
    和一些不平衡的{})并包括发送代码的相关部分?同样,可能很明显,但您使用的是TCP(不是UDP),对吗?您的问题可能会被暂时搁置,但别担心,只要稍微清理一下,它就可以重新打开。@Jason C,谢谢你们的建议和Jason C的编辑。很抱歉取消了您的编辑。我对stackoverflow的功能不太熟悉。但非常感谢您的努力。我正在重新编辑我的问题以澄清问题呃。@Jason C是的,它们是tcp套接字,我将添加这个。Tha