通过 WiFi-Direct 在两个 android 设备之间发送字符串
Send a string through WiFi-Direct between two android devices
我尝试在两个 android 设备之间使用 WiFi-Direct 发送字符串已经一个多月了,但我仍在努力理解我做错了什么。
我浏览过论坛,但他们通常不会提供太多关于如何实现我想要的细节。
我还浏览了 android 开发者网站上的那两个指南:
我正在使用一个 activity - ActivityConnection
- 我根据用户之前选择发送还是接收字符串来切换视图的可见性。
立即,在客户端,discoverPeers()
查找任何打开了 WiFi-Direct 的设备并将它们显示在 ListView
上。一旦用户选择了一个设备并按下发送按钮,连接就会自动建立并发送字符串。
在服务器端,使用我的 AsyncServerTask
class 立即启动服务器。在那里,它等待客户端连接并检索其发送的字符串。
我的主要问题是,在选择设备并点击发送按钮后,服务器端没有收到任何东西。
我的第二个问题是,有时设备未被发现并且列表视图保持为空。
我错过了什么吗?或者做错了什么?
这是我当前的代码。
我冒昧地删除了我认为断章取义的任何一行,以使其更易于阅读。
ActivityConnection
public class ActivityConnection extends AppCompatActivity implements NewPeersListener {
public static final String CONNECTION_ACTOR = "actor";
public static final String SEND_INFO = "send";
public static final String RECEIVE_INFO = "receive";
ListView listViewDevices;
private IntentFilter intentFilter;
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private WiFiDirectBroadcastReceiver receiver;
public List <WifiP2pDevice> listDevices;
private WifiP2pDevice selectedDevice;
@Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connectToSelectedDevice();
}
});
Intent intent = this.getIntent();
String actor = intent.getStringExtra(CONNECTION_ACTOR);
this.intentFilter = new IntentFilter();
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
this.manager = (WifiP2pManager) this.getSystemService(Context.WIFI_P2P_SERVICE);
this.channel = this.manager.initialize(this, this.getMainLooper(), null);
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.listDevices = new ArrayList <> ();
if (actor.equals(SEND_INFO)) {
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
this.listViewDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectedDevice = listDevices.get(position);
}
});
this.discoverPeers();
}
else if (actor.equals(RECEIVE_INFO)) {
new ServerAsyncTask(this).execute();
}
}
@Override
protected void onResume() {
super.onResume();
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.registerReceiver(this.receiver, this.intentFilter);
}
@Override
protected void onPause() {
super.onPause();
this.unregisterReceiver(this.receiver);
}
public void resultReceived (String result) {
Toast.makeText(ActivityConnection.this, "Received! :)", Toast.LENGTH_SHORT).show();
}
private void discoverPeers () {
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
// The discovery process succeeded
}
@Override
public void onFailure(int reason) {
// The discovery process DID NOT succeed
Toast.makeText(ActivityConnection.this, "Discovery process DID NOT succeed. Please verify that WiFi-Direct is active.", Toast.LENGTH_LONG).show();
}
});
}
private void connectToSelectedDevice () {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = this.selectedDevice.deviceAddress;
this.manager.connect(this.channel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
// Send string
Intent serviceIntent = new Intent(ActivityConnection.this, TransferService.class);
serviceIntent.setAction(TransferService.ACTION_SEND_STRING);
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_PORT, 8090);
startService(serviceIntent);
onBackPressed();
}
@Override
public void onFailure(int reason) {
Toast.makeText(ActivityConnection.this, "Connection failed. Try again.", Toast.LENGTH_SHORT).show();
}
});
}
@NonNull
private String getMacAddress () {
try {
List <NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder result = new StringBuilder();
for (byte b : macBytes) {
result.append(String.format("%02X:",b));
}
if (result.length() > 0) {
result.deleteCharAt(result.length() - 1);
}
return result.toString();
}
} catch (Exception e) {
}
return "02:00:00:00:00:00";
}
@Override
public void newPeers (WifiP2pDeviceList wifiP2pDeviceList) {
this.listDevices = new ArrayList <> (wifiP2pDeviceList.getDeviceList());
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
}
}
WiFiDirectBroadcastReceiver
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private ActivityConnection activity;
private List <NewPeersListener> listeners;
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, ActivityConnection activity) {
super();
this.manager = manager;
this.channel = channel;
this.activity = activity;
this.listeners = new ArrayList <> ();
this.listeners.add(activity);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wi-Fi P2P is enabled
} else {
// Wi-Fi P2P is not enabled
Toast.makeText(this.activity, "Please turn on WiFi-Direct (or WiFi-P2P).", Toast.LENGTH_SHORT).show();
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Request available peers from the wifi p2p manager.
if (this.manager != null) {
this.manager.requestPeers(this.channel, new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
for (NewPeersListener listener : listeners) {
listener.newPeers(peers);
}
}
});
}
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Respond to new connection or disconnections
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
// Respond to this device's wifi state changing
}
}
}
ServerAsyncTask(服务器)
public class ServerAsyncTask extends AsyncTask<Void, Void, String> {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataInputStream stream;
private WeakReference <Context> contextWeakReference;
ServerAsyncTask (Context context) {
this.contextWeakReference = new WeakReference <> (context);
}
@Override
protected String doInBackground (Void... params) {
try {
this.serverSocket = new ServerSocket(8090);
this.clientSocket = this.serverSocket.accept();
this.stream = new DataInputStream(this.clientSocket.getInputStream());
String received = this.stream.readUTF();
this.serverSocket.close();
return received;
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
return null;
} finally {
if (this.stream != null) {
try {
this.stream.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.clientSocket != null) {
try {
this.clientSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.serverSocket != null) {
try {
this.serverSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
}
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute (String result) {
super.onPostExecute(result);
((ActivityConnection) this.contextWeakReference.get()).resultReceived(result);
}
}
TransferService(客户端)
public class TransferService extends IntentService {
public static final String TAG = "WIFI_DIRECT";
private static final int SOCKET_TIMEOUT = 5000;
public static final String ACTION_SEND_STRING = "sendString";
public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";
public TransferService (String name) {
super(name);
}
public TransferService () {
super("TransferService");
}
@Override
protected void onHandleIntent (Intent intent) {
Context context = getApplicationContext();
if (intent.getAction().equals(ACTION_SEND_STRING)) {
String toSend = "string to send";
String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
Socket socket = null;
DataOutputStream stream = null;
try {
// Create a client socket with the host, port, and timeout information.
socket = new Socket();
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
Log.d(TAG, "Client connected socket - " + socket.isConnected());
// Send string
stream = new DataOutputStream(socket.getOutputStream());
stream.writeUTF(toSend);
stream.close();
Toast.makeText(context, "Sent! :)", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
在 ActivityConnetion
class 中,我给出的是 MAC 地址而不是 IP 地址。我不知道为什么我没有看到这个,也不知道为什么我一开始就这样做了。所以我环顾了这个论坛,找到了如何获取 WiFi-Direct 组所有者的 IP 地址:Wifi Direct Group Owner Address .
为了获取代码 运行,我进入了 ActivityConnetion
class,删除了 getMacAddress()
方法并替换了这一行:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
这一行:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, "192.168.49.1");
由于群主的IP一直不变,可以直接记下来。如果 IP 更改,这样做可能会停止工作,因此我建议改为查找组所有者的 IP。上面的 link 显示了如何操作。
我尝试在两个 android 设备之间使用 WiFi-Direct 发送字符串已经一个多月了,但我仍在努力理解我做错了什么。
我浏览过论坛,但他们通常不会提供太多关于如何实现我想要的细节。
我还浏览了 android 开发者网站上的那两个指南:
我正在使用一个 activity - ActivityConnection
- 我根据用户之前选择发送还是接收字符串来切换视图的可见性。
立即,在客户端,
discoverPeers()
查找任何打开了 WiFi-Direct 的设备并将它们显示在ListView
上。一旦用户选择了一个设备并按下发送按钮,连接就会自动建立并发送字符串。在服务器端,使用我的
AsyncServerTask
class 立即启动服务器。在那里,它等待客户端连接并检索其发送的字符串。
我的主要问题是,在选择设备并点击发送按钮后,服务器端没有收到任何东西。 我的第二个问题是,有时设备未被发现并且列表视图保持为空。
我错过了什么吗?或者做错了什么?
这是我当前的代码。 我冒昧地删除了我认为断章取义的任何一行,以使其更易于阅读。
ActivityConnection
public class ActivityConnection extends AppCompatActivity implements NewPeersListener {
public static final String CONNECTION_ACTOR = "actor";
public static final String SEND_INFO = "send";
public static final String RECEIVE_INFO = "receive";
ListView listViewDevices;
private IntentFilter intentFilter;
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private WiFiDirectBroadcastReceiver receiver;
public List <WifiP2pDevice> listDevices;
private WifiP2pDevice selectedDevice;
@Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connectToSelectedDevice();
}
});
Intent intent = this.getIntent();
String actor = intent.getStringExtra(CONNECTION_ACTOR);
this.intentFilter = new IntentFilter();
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
this.manager = (WifiP2pManager) this.getSystemService(Context.WIFI_P2P_SERVICE);
this.channel = this.manager.initialize(this, this.getMainLooper(), null);
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.listDevices = new ArrayList <> ();
if (actor.equals(SEND_INFO)) {
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
this.listViewDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectedDevice = listDevices.get(position);
}
});
this.discoverPeers();
}
else if (actor.equals(RECEIVE_INFO)) {
new ServerAsyncTask(this).execute();
}
}
@Override
protected void onResume() {
super.onResume();
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.registerReceiver(this.receiver, this.intentFilter);
}
@Override
protected void onPause() {
super.onPause();
this.unregisterReceiver(this.receiver);
}
public void resultReceived (String result) {
Toast.makeText(ActivityConnection.this, "Received! :)", Toast.LENGTH_SHORT).show();
}
private void discoverPeers () {
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
// The discovery process succeeded
}
@Override
public void onFailure(int reason) {
// The discovery process DID NOT succeed
Toast.makeText(ActivityConnection.this, "Discovery process DID NOT succeed. Please verify that WiFi-Direct is active.", Toast.LENGTH_LONG).show();
}
});
}
private void connectToSelectedDevice () {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = this.selectedDevice.deviceAddress;
this.manager.connect(this.channel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
// Send string
Intent serviceIntent = new Intent(ActivityConnection.this, TransferService.class);
serviceIntent.setAction(TransferService.ACTION_SEND_STRING);
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_PORT, 8090);
startService(serviceIntent);
onBackPressed();
}
@Override
public void onFailure(int reason) {
Toast.makeText(ActivityConnection.this, "Connection failed. Try again.", Toast.LENGTH_SHORT).show();
}
});
}
@NonNull
private String getMacAddress () {
try {
List <NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder result = new StringBuilder();
for (byte b : macBytes) {
result.append(String.format("%02X:",b));
}
if (result.length() > 0) {
result.deleteCharAt(result.length() - 1);
}
return result.toString();
}
} catch (Exception e) {
}
return "02:00:00:00:00:00";
}
@Override
public void newPeers (WifiP2pDeviceList wifiP2pDeviceList) {
this.listDevices = new ArrayList <> (wifiP2pDeviceList.getDeviceList());
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
}
}
WiFiDirectBroadcastReceiver
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private ActivityConnection activity;
private List <NewPeersListener> listeners;
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, ActivityConnection activity) {
super();
this.manager = manager;
this.channel = channel;
this.activity = activity;
this.listeners = new ArrayList <> ();
this.listeners.add(activity);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wi-Fi P2P is enabled
} else {
// Wi-Fi P2P is not enabled
Toast.makeText(this.activity, "Please turn on WiFi-Direct (or WiFi-P2P).", Toast.LENGTH_SHORT).show();
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Request available peers from the wifi p2p manager.
if (this.manager != null) {
this.manager.requestPeers(this.channel, new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
for (NewPeersListener listener : listeners) {
listener.newPeers(peers);
}
}
});
}
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Respond to new connection or disconnections
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
// Respond to this device's wifi state changing
}
}
}
ServerAsyncTask(服务器)
public class ServerAsyncTask extends AsyncTask<Void, Void, String> {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataInputStream stream;
private WeakReference <Context> contextWeakReference;
ServerAsyncTask (Context context) {
this.contextWeakReference = new WeakReference <> (context);
}
@Override
protected String doInBackground (Void... params) {
try {
this.serverSocket = new ServerSocket(8090);
this.clientSocket = this.serverSocket.accept();
this.stream = new DataInputStream(this.clientSocket.getInputStream());
String received = this.stream.readUTF();
this.serverSocket.close();
return received;
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
return null;
} finally {
if (this.stream != null) {
try {
this.stream.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.clientSocket != null) {
try {
this.clientSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.serverSocket != null) {
try {
this.serverSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
}
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute (String result) {
super.onPostExecute(result);
((ActivityConnection) this.contextWeakReference.get()).resultReceived(result);
}
}
TransferService(客户端)
public class TransferService extends IntentService {
public static final String TAG = "WIFI_DIRECT";
private static final int SOCKET_TIMEOUT = 5000;
public static final String ACTION_SEND_STRING = "sendString";
public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";
public TransferService (String name) {
super(name);
}
public TransferService () {
super("TransferService");
}
@Override
protected void onHandleIntent (Intent intent) {
Context context = getApplicationContext();
if (intent.getAction().equals(ACTION_SEND_STRING)) {
String toSend = "string to send";
String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
Socket socket = null;
DataOutputStream stream = null;
try {
// Create a client socket with the host, port, and timeout information.
socket = new Socket();
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
Log.d(TAG, "Client connected socket - " + socket.isConnected());
// Send string
stream = new DataOutputStream(socket.getOutputStream());
stream.writeUTF(toSend);
stream.close();
Toast.makeText(context, "Sent! :)", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
在 ActivityConnetion
class 中,我给出的是 MAC 地址而不是 IP 地址。我不知道为什么我没有看到这个,也不知道为什么我一开始就这样做了。所以我环顾了这个论坛,找到了如何获取 WiFi-Direct 组所有者的 IP 地址:Wifi Direct Group Owner Address .
为了获取代码 运行,我进入了 ActivityConnetion
class,删除了 getMacAddress()
方法并替换了这一行:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
这一行:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, "192.168.49.1");
由于群主的IP一直不变,可以直接记下来。如果 IP 更改,这样做可能会停止工作,因此我建议改为查找组所有者的 IP。上面的 link 显示了如何操作。