问题描述
我目前正在构建一个应用程序,目的是通过 WiFi 直接将视频从 Linux 设备流式传输到 Android 设备。我目前使用 raspBerry pi 作为 Linux 设备,它也将是 wifi-direct 组所有者 (GO),因此它将被视为用于网络目的的服务器。在这种情况下,Android 设备将被视为客户端。
WiFi 直连的工作原理是客户端只知道组所有者的 IP 地址,我需要向 GO 提供 Android 的 IP,以便它可以开始通过已建立的 WiFi 直连发送 UDP 数据包。在这种情况下,Android 也将充当控制器,并且还需要知道 Pi 的 IP 地址才能向其发送命令。
在建立此连接后,Android 设备会向 pi 发送一个数据报数据包作为 ping。这样 pi 就可以记录 Android 的 IP。这就是我的问题所在。Pi 成功接收到这个数据包,并尝试将响应发送回 Android,让它知道它已经记录了 IP 地址。但是,Android 永远不会收到这个返回数据包!我仍然是一个网络菜鸟,所以在涉及这些类型的事情时我真的没有太多的调试技巧。
此代码在建立WiFi直连后在树莓派上执行;它的工作是从 Android 接收第一个 ping 并在 10 秒内发送 10 个数据包作为响应:
编辑:这会在端口 8988 上创建一个套接字。Pi 的静态 IP 地址为 192.168.4.1/24,并作为 DHCP 服务器运行。
#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>
#define BACKLOG 2 //Allowed connection count.
#define PORT "8988" //The port we will be listening on.
#define MAX_RECV 1024
/**
* 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,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] = '\0';
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,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;
}
Android 端遵循相同的概念,但是每次调用 receive()
时都会超时。出于调试目的,RECEIVE_TIMEOUT_MS
设置为 20000(20 秒)。每次调用此代码时,Pi 都会收到第一条消息,但响应永远不会到达 Android 设备。因此,它只会尝试一次这种假握手; Pi 会已经发出它的确认数据包。注意int MAX_HANDSHAKE_ATTEMPTS = 1
。代码当前包含在后台运行的线程中:
编辑: Java 代码创建了一个绑定到端口 8988 的 DatagramSocket,当前允许系统选择一个有效的 IP。这个IP往往是192.168.4.163
public static final int PORT = 8988;
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);
}
我对这里的问题感到困惑。在发送信息的 10 秒内,Android 应该至少收到一个由 Pi 发送的数据报数据包,但它们似乎从未到达 Android!任何帮助将不胜感激。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)