【问题标题】:Android TCP connection receiving packets out of orderAndroid TCP连接接收数据包乱序
【发布时间】:2016-12-16 20:50:05
【问题描述】:

我有一个 Particle Photon 微控制器通过热点 WiFi 网络向 Android 手机发送 TCP 数据包。微控制器充当服务器,手机充当客户端。

然而,尽管信息是通过 tcp 传输的,但手机仍会乱序接收一些(但不是全部)数据包。我的理解是这不会发生 - 我错了吗,或者我可以做些什么来纠正这个问题?

微控制器代码:

// This #include statement was automatically added by the Particle IDE.
#include "databuffer5.h"

// This #include statement was automatically added by the Particle IDE.
#include "databuffer7.h"





const unsigned int localPort = 10000;
IPAddress remoteIP(a, b, c, d);
// An TCP instance to let us send and receive packets over wifi
TCPServer server = TCPServer(localPort);
TCPClient client;

// UDP Port used for two way communication


short msg_count = 0;

const int adcPin = A0; 


int byteBuffer;

unsigned long loopTimer;

const int packetSize = 40;  //number of bytes in packet - 10 ints with 4 bytes each
byte buffer[packetSize]; 

int j = 0;
int dataCount = 0;  //dummy data that increments every loop point, to measure packet contiuity 
                    //(creates a line with slope 1 as data)





void setup() {
  // start the UDP
  server.begin();


  // Print your device IP Address via serial
  Serial.begin(9600);
  Serial.println(WiFi.localIP()); 
  //Serial.println(System.ticksPerMicrosecond());   //returns 120, ie 120MHz


}



void loop() 
{
  if (client.connected())
  {
    loopTimer = millis();  //mark start time of loop

      for (int i = 0; i < 10; i++)
      {



          //for testing connection
          byteBuffer = i+j*10;

          buffer[i*4]   = ( (byteBuffer  >> 24) & 0xFF);     //take upper 8 bits  
          buffer[i*4+1] = ( (byteBuffer  >> 16) & 0xFF);     //take middle upper 8 bits  
          buffer[i*4+2] = ( (byteBuffer  >> 8 ) & 0xFF);     //take middle lower 8 bits 
          buffer[i*4+3] = (  byteBuffer         & 0xFF);     //take lower 8 bits
          dataCount++;

          if (i != 9) 
          {
              while(millis() < (loopTimer+10*(i+1)));  //ie do nothing for 10 ms (time is in ms, want to delay by exactly 10ms for each loop)

                                                        //goal here is to sample every 10ms, by delaying for the remaining time 
                                                        //dont delay here for the last sample, as the udp packet will take time
                                                        //delay after instead
          }

      }


      server.write(buffer, sizeof(buffer)); //using sizeof on a byte array so dont need to scale (ie scaling factor is 1)
      j++; 
      if (j < 0){j = 0;}
      while(millis() < (loopTimer+10*10));  //delay till 100ms after loops started
   }
   else 
   {
       client = server.available();
   }
}

Android(客户端)代码:

async_udp = new AsyncTask<Void, int[], Void>() {
            @Override
            protected Void doInBackground(Void... params) {


                byte b1[];
                b1 = new byte[100];

                while (serverActive) {
                    Socket socket = null;   //previously this was DatagramSocket (UDP) - no Socket (TCP)
                    try {
                        //DatagramSocket s = new DatagramSocket(server_port, server_ip);
                        socket = new Socket(server_ip, server_port);
                        socket.setPerformancePreferences(1, 2, 2);

                        InputStream socketStream = socket.getInputStream();



                        DatagramPacket p1 = new DatagramPacket(b1, b1.length);


                        ByteBuffer wrapped;
                        int data[] = new int[10+1];  //first number for message data, second is for the message number

                        while (serverActive)   //TODO include shutdown function
                        {
                            while (socketStream.available() < 39){}

                            socketStream.read(b1, 0, 40);

                            //packet structure is a char containing message number, and 10  shorts (2 bytes) containing data points (between 0 and 4096)


                            wrapped = ByteBuffer.wrap(Arrays.copyOfRange(b1, 0, 40)); // extract 40 bytes to convert to 10 ints
                            for (int i = 0; i < 10; i++) {
                                data[i] = wrapped.getInt();
                            }


                            String str = data.toString();

                            server_port = p1.getPort();
                            server_ip = p1.getAddress();


                            String str_msg = "RECEIVED FROM CLIENT IP =" + server_ip + " port=" + server_port + " message no = " + b1[0] +
                                    " data=" + str;  //first character is message number


                            statusText = str_msg;
                            publishProgress(data);

                        }
                        socketStream.close();
                        socket.close();

                    } catch (SocketException e) {
                        if (socket != null) {}

                        //status.append("Error creating socket");
                        statusText = (" Error creating socket");   //this doesn't work!
                    } catch (IOException e) {
                        //status.append("Error recieving packet");
                        statusText = (" Error receiving packet");  //this doesn't work!
                    }
                    try{
                    Thread.sleep(100, 0);  //sleep for 10ms if no wifi lock is found, to stop battery being silly
                    } catch(Exception e){
                        e.printStackTrace();
                    }
                }

                return null;
            }

            protected void onProgressUpdate(int[]... data1)
            {
                super.onProgressUpdate(data1);
                int data[] = data1[0];

                //send data to graph

                for (int i = 0; i < 9; i++)
                {
                    series.appendData(new DataPoint(lastDataX++, data[i]), false, graphPointsMax);
                    //append 9 points to graph, but only redraw the grpah on the 10th
                }
                series.appendData(new DataPoint(lastDataX++, data[9]), true, graphPointsMax);



            }

        };

        if (Build.VERSION.SDK_INT >= 11)
        {
            async_udp.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
        else
        {
            async_udp.execute();
        }

【问题讨论】:

    标签: android tcp wifi microcontroller


    【解决方案1】:

    TCP 保证的是,当您发送消息时 - 它会以正确的顺序出现,即使沿途被 TCP 堆栈拆分(请参阅 TCP 段号,它作为拆分消息部分的指示属于哪个,并以什么顺序)。因此,就您而言,您发送的所有内容都使用:

    server.write(buffer, sizeof(buffer));

    不知道缓冲区有多大(当然是在 TCP 协议的限制范围内)保证以正确的顺序到达并完成。

    通过 TCP 一个接一个地发送多个缓冲区将为您提供有保证的传递或错误通知(如果有),但不是发送消息的顺序。在协议层面,这些数据包将包含不同的Sequence Numbers,因此将由接收方的 TCP 堆栈按照堆栈偏好的顺序分别处理。

    我看到在这里可以做两件相对简单的事情:

    1. 尽量将所有可能的内容放在一个缓冲区中(虽然不是很实用的方法)
    2. 在每次发送缓冲区时引入计数器并递增。将计数器与缓冲区放在一起,并检查接收方的优先级(电话,在您的情况下)。

    【讨论】:

    • 选项 1 对我没有好处,因为我试图尽可能减少延迟。选项 2 是否有任何标准库/解决方案?我尝试自己实现它,但作为一名自学成才的程序员,我没有足够的技能在我有的时候做到这一点
    • 这个答案是完全错误的! TCP 不关心顶端的“消息”,它完全面向流并保证所有接收字节(或通信失败)的正确顺序。请参阅en.m.wikipedia.org/wiki/… 的第 5 段。我不知道为什么 OP 有问题,但这不是原因。
    • @DoxyLover - 你说得对,TCP 与消息无关(就 OSI 模型而言)。我只是尝试使用主题启动器的术语。通过我的回答中的消息,应该理解单独的数据包(或流的一部分,如果您更愿意概括它)。致帖子所有者-尝试我的建议有什么运气吗?
    • @OpalApps 是的,“在线”数据包可以按任何顺序排列。但是,这对用户实时代码无关紧要。 TCP 将重组传入的数据包,以保证在用户级别,您将按发送顺序接收所有字节,而不管用户级别发送方如何打包数据。一次写入一个字节还是一次写入 10,000 个字节都没有关系。这是 TCP 的基础,如果 Android 或发送微不这样做,其中一个就坏了(我不认为是 Android)!
    • @DoxyLover,同意。而且我怀疑微型方面的图书馆混合了订单。但是从问题的声音来看 - 流的组装发生在 Android 端。可能是 MCU 端的库忽略了序列号或插入错误。流量转储将有助于了解更多信息。
    猜你喜欢
    • 1970-01-01
    • 2017-09-20
    • 2012-09-27
    • 2018-11-07
    • 2020-09-28
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多