为什么我可以接收广播,但无法与App中的特定套接字通信?

时间:2018-09-07 16:51:41

标签: java android udp broadcast

首先,有关我的设置的一些信息。 我有一个S8手机,我根据Google的AR-Devkit演示在其中运行此应用程序。

  public void closeSocket(DatagramSocket socket) {

    if (socket != null && socket.isConnected() ) {
        while (!socket.isConnected()) {
            socket.disconnect();
            try {
                Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
            } catch (InterruptedException e) {
                Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
                e.printStackTrace();
            }
        }
    }

    if (socket != null && !socket.isClosed()) {
        socket.close();
        while (!socket.isClosed()) {
            try {
                Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
            } catch (InterruptedException e) {
                Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
                e.printStackTrace();
            }
        }
    }
}

public DatagramSocket createSocket(InetAddress ipAddress, int port) {
    try {
        DatagramSocket socket = new DatagramSocket(null);
        InetSocketAddress address = new InetSocketAddress(ipAddress, port);
        socket.setReuseAddress(true);
        socket.bind(address);

        return socket;

    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public DatagramSocket getBroadcastListenerSocket() throws IOException {

    InetSocketAddress anyAdress = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 9000);
    DatagramSocket socket = new DatagramSocket(null);
    socket.setSoTimeout(30);
    socket.setReuseAddress(true);
    socket.bind(anyAdress);
    return socket;
}

public DatagramSocket getBroadcastSenderSocket(DatagramSocket oldSocket) {

    DatagramSocket socket = null;
    try {
        ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
        socket = getSocket(oldSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
        socket.setBroadcast(true);
        socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return socket;
}

public DatagramSocket getSocket(DatagramSocket oldSocket, InetAddress ipAddress, int port, InetAddress targetAddress) {

    if (oldSocket != null ) {
        closeSocket(oldSocket);
    }
    DatagramSocket socket = null;
    try {
        socket = createSocket(ipAddress, port);
        socket.setBroadcast(false);
        socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
        if (targetAddress != null)
          socket.connect(targetAddress, port);

    } catch (SocketException e) {
        e.printStackTrace();
    }
    return socket;
}

 public class DatagramReciever extends Thread {

        private String datagramToSend = "";
        private boolean newDatagramToSend = false;
        private DatagramPacket snd_packet;

        DatagramSocket senderSocket = null;
        DatagramSocket listenerSocket = null;
        private DatagramSocket broadCastListenerSocket;

        //Buffer gettters and setters
        private int writeBuffer = 0;
        private SpringAR.comStates oldState;

        int getReadBuffer() {
            if (writeBuffer == 1) return 0;
            return 1;
        }

        void switchBuffer() {
            recieveByteIndex = 0;
            writeBuffer = getReadBuffer();
        }

        public String dbg_message = "";
        //Management Communication Headers

        public void kill() {
            closeSocket(senderSocket);
            closeSocket(listenerSocket);
            closeSocket(broadCastListenerSocket);
        }



        public void run() {

            try {

                initializeBroadcastConnection();

                while (true) {

                    //Recieving Datagramm
                    DatagramPacket rcv_packet = new DatagramPacket(rcv_message[writeBuffer], rcv_message[writeBuffer].length);
                    boolean NewMessageArrived = true;
                    try {
                        listenerSocket.receive(rcv_packet);
                    } catch (SocketTimeoutException e) {
                          NewMessageArrived = false;
                    }
                    //Watchdog
                    handleWatchDogTimer(State);

                    //TODO Delete String conversion
                    if (NewMessageArrived) {
                        dbg_message = new String(rcv_message[writeBuffer], 0, rcv_packet.getLength(), "US-ASCII");
                        Log.d(SpringAR.dataDebugLogPrefix, "" + rcv_packet.getAddress().getHostAddress() + ": " + dbg_message.trim() + " of " + rcv_packet.getLength() + "length ");
                    }

                    if (validatePackageSender(rcv_packet)) {
                        connectionStateMachine(rcv_message, rcv_packet);
                    }

                    //Sending Datagram
                    if (newDatagramToSend && hostIpAddress != null) {
                        //Log.d(SpringAR.protocollDebugLogPrefix, "Server sending: " + datagramToSend);
                        byte[] snd_message = datagramToSend.getBytes();

                        try {
                            snd_packet = packSendPackageByState(snd_message);
                            assert (snd_packet != null);
                            senderSocket.send(snd_packet);
                            newDatagramToSend = false;
                        } catch (IOException e1) {
                            e1.printStackTrace();
                            //causes     Caused by: android.system.ErrnoException: sendto failed: EINVAL (Invalid argument)
                            Log.e(SpringAR.protocollDebugLogPrefix, "Server Error in State: " + State.name());
                           break;
                        }

                    }

                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        private void initializeBroadcastConnection() throws IOException {
            ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
            senderSocket = getSocket(null, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
            broadCastListenerSocket = getBroadcastListenerSocket();
            listenerSocket = broadCastListenerSocket;
            Log.d(SpringAR.protocollDebugLogPrefix, "initializeBroadcastConnection completed");
        }


        // handles management traffic like configurstion files
        private void connectionStateMachine(byte[][] payload, DatagramPacket rcv_packet) throws IOException {
            //Reset triggered by Host
            if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveResetHeaderByte) != SpringAR.STRING_NOT_FOUND) {
                State = SpringAR.comStates.STATE_resetCommunication;
            }

            Log.d(SpringAR.protocollDebugLogPrefix, "ConnectionStateMachine: " + State.name());
            switch (State) {
                case STATE_resetCommunication: {
                    messageCounter = 0;
                    listenerSocket = broadCastListenerSocket;
                    hostIpAddress = comonUtils.getBroadcastAddress(context);
                    senderSocket = getBroadcastSenderSocket(senderSocket);
                    setSendToSpringMessage(SpringAR.sendResetHeader);
                    State = SpringAR.comStates.STATE_broadCastHeader;

                    return;
                }

                case STATE_broadCastHeader: {
                    if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveHostReplyHeaderByte) != SpringAR.STRING_NOT_FOUND) {
                        Log.d(SpringAR.protocollDebugLogPrefix, " Host Reply Header recieved");
                        //Extract the hostIp
                        String hostIpAdressAsString = new String(payload[writeBuffer]);
                        hostIpAdressAsString = hostIpAdressAsString.replace(SpringAR.recieveHostReplyHeader, "").trim();
                        Log.d(SpringAR.dataDebugLogPrefix, hostIpAdressAsString);

                        hostIpAddress = InetAddress.getByName(hostIpAdressAsString);

                        //Set Connection from broadcast to target
                        ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
                        Log.d(SpringAR.protocollDebugLogPrefix, " New Device Adress " + ARDeviceAddress);
                        senderSocket = getSocket(senderSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, hostIpAddress);
                        listenerSocket = senderSocket;
                        State = SpringAR.comStates.STATE_sendCFG;
                        return;
                    }


                    setSendToSpringMessage(SpringAR.sendBroadcasteHeader);

                    delayByMs(SpringAR.TIME_OUT_IN_BROADCAST);
                    return;
                }

                case STATE_sendCFG: {
                    if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveCFGHeaderByte )) {

                        State = SpringAR.comStates.STATE_sendRecieveData;
                        return;
                    }

                    setSendToSpringMessage(SpringAR.formConfigurationMessage());
                    return;
                }

                case STATE_sendRecieveData: {
                    if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveDataHeaderByte)) {
                        writeRecievedDataToBuffer(payload[writeBuffer], rcv_packet.getLength());
                    }
                    break;
                }
                default:
                    Log.d(SpringAR.protocollDebugLogPrefix, "Connection State Machine invalid state");

            }


}

https://github.com/PicassoCT/arcore-android-sdk/blob/6c9b48a3d520e039cd48bc2af7354ccdec857736/arcore-android-sdk/samples/hello_ar/app/src/main/java/com/google/ar/core/examples/app/common/tcpClient/Server.java

所有测试都在家庭WiFi设置中进行,其中带有主机应用程序的桌面直接连接到WiFi路由器。

到目前为止,什么工作正常: 设备可以广播其状态。 主机可以广播其配置。 设备无法在主机上从IP到IP进行通信。双方都有固定的IP集。

我可以通过主机应用程序与App PacketSender通信,并排除了故障。

我还构建了一个较小的调试循环,仅来回发送udp数据包,这也有效。

谢谢您的时间

Captured Packages-Showing Broadcast going from the ARDevice to the Host-Application and a direktional Answer by the HostApplication, that is never recieved on the ARDevice

1 个答案:

答案 0 :(得分:1)

更改此行:

socket.setSoTimeout(30);

socket.setSoTimeout(1000);

这里有一个相当复杂的状态机,如果不查看日志就很难辨别正在发生的事情。我将像这样总结您的状态机:

  1. 广播配置消息
  2. 花费30毫秒聆听回应
  3. 如果未收到响应,请阻止SpringAR.TIME_OF_FRAME_IN_MS(代码中未包含;我认为是1000ms),然后循环回到#1
  4. 如果收到响应,则直接将回复发送给对等方,然后转到#2

#4是没有发生的步骤。可能的原因(基于Wireshark转储)是ARDevice的响应到达“主机”需要68毫秒。您只给了30ms。花费这么长时间可能有多种原因,但这超出了您的问题范围。