Android DatagramSocket unable to receive packet over WiFi-Direct

  android, c++, raspberry-pi, sockets, wifi-direct

I’m currently building an application with the intent of streaming video from a Linux device to an Android device over WiFi direct. I am currently using a raspberry pi as the Linux device, which will also be the WiFi-Direct group owner (GO), thus it will be treated as the server for networking purposes. The Android device will be treated as the client in this scenario.

WiFi direct works such that the client only knows the group owner’s IP address, and I need to supply the GO with the Android’s IP so it can start blasting UDP packets over the established WiFi direct connection. The Android will also be acting as a controller in this scenario, and also needs to know the Pi’s IP address to send it commands.

After establishing this connection, the Android device sends a Datagram packet serving as a ping to the pi. This is so the pi can take note of the Android’s IP. This is where my problem comes in. The Pi successfully receives this packet, and attempts to send a response back to the Android to let it know that it has taken note of IP address. However, the Android never receives this return packet! I’m still a networking noob, so I really don’t have much debugging skills when it comes to these types of things.

This code is executed on the raspberry pi after a WiFi direct connection is established; it’s job being receiving the first ping from the Android and sending a 10 packets over 10 seconds as a response:

#include"p2p_go_socket.h"

#include<cstddef>
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<thread>
#include<errno.h>
#include<chrono>

/**
 * Listens for a UDP (Datagram) message from our connected p2p client.
 * ***Blocks*** until receiving a message, upon recieving this message, a response is sent to the client.
 * Once the client receives this response, they are safe to assume we have stored their IP address for future
 * UDP based communication.
 */
bool udp_connection_handshake() {
    //Address info structure, used to open our initial socket.
    struct addrinfo *addr_info;

    struct sockaddr_in client_addr;

    //Fill the udp server hints of our
    int sock_fd = -1;
    if (fill_udp_addr_info(&addr_info)) {
        std::cout << "Opening socket..." << std::endl;
        sock_fd = open_socket(addr_info);
    }

    if (sock_fd < 0) {
        std::cout << "Error opening server socket: " << strerror(errno) << std::endl;
        return false;
    }

    ///Bind socket so we can send/receive information.
    if (bind(sock_fd, addr_info->ai_addr, addr_info->ai_addrlen) < 0) {
        close(sock_fd);
        std::cout << "Error binding server socket: " << strerror(errno) << std::endl;
        return false;
    }
    std::cout << "Binded successfully" << std::endl;

    ///Allocate memory for the peer info.
    memset(&client_addr, 0, sizeof client_addr);
    size_t len = sizeof client_addr;

    ///Buffer for receiving messages.
    char buffer[MAX_RECV];
    std::cout << "Listening for messages sent to us ... " << std::endl;

    //Try to receive a message from our client.
    int bytes_received = recvfrom(sock_fd, (char *) buffer, MAX_RECV, MSG_WAITALL, (struct sockaddr*) &client_addr, &len);
    if (bytes_received < 0) {
        close(sock_fd);
        return false;
    }
    buffer[bytes_received] = '{$content}';
    char client_ip[INET_ADDRSTRLEN];
    int client_port = ntohs((&client_addr)->sin_port);
    //debug code ----
    inet_ntop(AF_INET, &((&client_addr)->sin_addr), client_ip, INET_ADDRSTRLEN);
    std::cout << "Client Message: " << buffer << std::endl;
    std::cout << "Client information: " << std::endl;
    std::cout << "Client Addr = " << client_ip << std::endl;
    std::cout << "Client Port = " << client_port << std::endl;
    std::cout << "Sleeping for 500ms..." << std::endl;

    //We have received response :: 
    //Send out 10 confirmation messages over 10 seconds (ensure that client has open socket). 
    for (int i = 0; i < 10; i++) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        int sent = sendto(sock_fd, CONFIRM_CONNECTION_MSG.c_str(),
            CONFIRM_CONNECTION_MSG.length(), MSG_CONFIRM,
            (const struct sockaddr*) &client_addr, len);
        std::cout << "Sent " << sent << " bytes " << std::endl;
    }
    std::cout << "Sent confirmation messages." << std::endl;
    //Close the socket. We are clear to start sending new data to the client's IP.
    close(sock_fd);
    return true;
}

/**
 * Opens a socket given the parameterized addrinfo hints.
 * Return value is identical to the return value of socket(), the socket file descriptor, or -1 on error.
 * Simply a wrapper for socket()
 */
int open_socket(struct addrinfo* addr_info) {
    return socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
}

/**
 * Fills a addrinfo structure with the values indicating that
 * a socket opened with the values in the para structure will be a UDP server.
 * Returns true based on the address information being properly filled via "getaddrinfo()"
 */
bool fill_udp_addr_info(struct addrinfo** addr_info) {
    //Populate hint structure.
    struct addrinfo hints;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE;
    //Fill address info structure with
    return getaddrinfo(nullptr, PORT, &hints, addr_info) == 0;
}

The same concept follows on the Android side, however each time receive() is called, it times out. For debugging purposes, RECEIVE_TIMEOUT_MS is set to 20000 (20 seconds). Every time this code is called, the Pi receives the first message, however the response never reaches the Android device. Because of this, it will only attempt this faux handshake once; the Pi would have already sent out it’s confirmation packets. Note int MAX_HANDSHAKE_ATTEMPTS = 1. Code is currently wrapped in a thread that is ran in the background:

private static final int RECEIVE_TIMEOUT_MS = 20000; //20 seconds...
    
private static final int MAX_HANDSHAKE_ATTEMPTS = 1;
    
private static final int MAX_BUFFER_SIZE = 512;
    
private final WifiP2pInfo mGroupInfo;
    
private final WifiDirectService mWifiDirectService;

private final MutableLiveData<Integer> mReceivedResponseFlag = new MutableLiveData<>(FLAG_WAITING);

/* 
**
** unrelated code here 
**
*/

private Thread getExchangeWorker() {
    Runnable r = () -> {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(PORT);
            int connectAttempts = 0;
            byte[] message = INIT_MESSAGE.getBytes();
            byte[] buffer = new byte[MAX_BUFFER_SIZE];
            //Wait for RECEIVE_TIMEOUT_MS ms for a response to be sent back.
            socket.setSoTimeout(RECEIVE_TIMEOUT_MS);
            while (connectAttempts < MAX_HANDSHAKE_ATTEMPTS) {
                DatagramPacket sendPacket = new DatagramPacket(message, message.length, mGroupInfo.groupOwnerAddress, PORT);
                socket.send(sendPacket);
                DatagramPacket receivePacket = new DatagramPacket(buffer,  buffer.length);
                try {
                    socket.receive(receivePacket);
                    if (receivePacket.getLength() > 0) { //We have received a response, note it!
                        mReceivedResponseFlag.postValue(FLAG_RECEIVED);
                        String receivedResponse = Arrays.toString(buffer);
                        Log.i(TAG, "Received message from GO: " + receivedResponse);
                        break; //We have received a packet. Break!
                    }
                } catch (SocketTimeoutException e) {
                    Log.e(TAG, "Receive timed out on connect attempt = " + (connectAttempts + 1));
                }
                connectAttempts++;
            }
        } catch (IOException e) {
            mReceivedResponseFlag.postValue(FLAG_ERROR);
            Log.e(TAG, "IO error inside of the client sender thread.", e);
        }
        if (socket != null)
            socket.close();
    };
    return new Thread(r);
}

I’m stumped as to what the issue is here. The Android should be receiving at least ONE of the datagram packets sent out by the Pi over the 10 seconds it is sending out information, however they never seem to reach the Android! Any help would be greatly appreciated.

Source: Windows Questions C++

LEAVE A COMMENT