使用 wifi 直接在 Android 设备之间传输数据?

Posted

技术标签:

【中文标题】使用 wifi 直接在 Android 设备之间传输数据?【英文标题】:Transfer Data between Android devices using wifi direct? 【发布时间】:2013-07-18 05:36:37 【问题描述】:

我最近在我的项目中实现了 wifi direct,我的目标是当我的一些应用程序条件满足时,在两个 wifidirect 连接的设备之间传递字符串值。现在我已经列出了所有对等点,并在选定的对等点之间建立了连接。我的想法是在设备之间传递一个 json 文件。所以在尝试执行该想法之前,我尝试在两个设备之间传递一个图像文件。我遵循了 android wifidirect 教程中的步骤。列出对等点,对等点之间的连接是成功的但我无法在设备之间传递数据。以下是我的代码。 FileTransferService.java

package jing.app.directwifi;

import android.app.IntentService;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
 import android.util.Log;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;


/**
 * A service that process each file transfer request i.e Intent by opening a
 * socket connection with the WiFi Direct Group Owner and writing the file
 */
public class FileTransferService extends IntentService 

private static final int SOCKET_TIMEOUT = 5000;
public static final String ACTION_SEND_FILE = "jing.app.directwifi.SEND_FILE";
public static final String EXTRAS_FILE_PATH = "file_url";
public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";

public FileTransferService(String name) 
    super(name);


public FileTransferService() 
    super("FileTransferService");


/*
 * (non-Javadoc)
 * @see android.app.IntentService#onHandleIntent(android.content.Intent)
 */
@Override
protected void onHandleIntent(Intent intent) 

    Context context = getApplicationContext();
    if (intent.getAction().equals(ACTION_SEND_FILE)) 
        String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);
        String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
        Socket socket = new Socket();
        int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);

        try 
           // Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");
            socket.bind(null);
            socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);

            Log.d("connected", "Client socket - " + socket.isConnected());
            OutputStream stream = socket.getOutputStream();
            ContentResolver cr = context.getContentResolver();
            InputStream is = null;
            try 
                is = cr.openInputStream(Uri.parse(fileUri));
             catch (FileNotFoundException e) 
                Log.d("exp", e.toString());
            
           MainActivity.copyFile(is, stream);
            Log.d("exp" ,"Client: Data written");
         catch (IOException e) 
            Log.e("exp", e.getMessage());
         finally 
            if (socket != null) 
                if (socket.isConnected()) 
                    try 
                        socket.close();
                     catch (IOException e) 
                        // Give up
                        e.printStackTrace();
                    
                
            
        

    


主Activity.java

package jing.app.directwifi;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;





import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
 import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.ActionListener;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
import android.os.AsyncTask;
import android.os.Bundle;
 import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener,   android.content.DialogInterface.OnClickListener, ConnectionInfoListener 

private WifiP2pManager mManager;
private Channel mChannel;
private BroadcastReceiver mReceiver;
private IntentFilter mIntentFilter;

private Button mDiscover;
private TextView mDevices;
public ArrayAdapter mAdapter;
private ArrayList<WifiP2pDevice> mDeviceList = new ArrayList<WifiP2pDevice>();
protected static final int CHOOSE_FILE_RESULT_CODE = 20;
int flag=0;
@Override
protected void onCreate(Bundle savedInstanceState) 

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mDiscover = (Button) findViewById(R.id.discover);
    mDiscover.setOnClickListener(this);

    mDevices = (TextView) findViewById(R.id.peers);

    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(this, getMainLooper(), null);
    mReceiver = new WiFiDirectReceiver(mManager, mChannel, this);

    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);




@Override
protected void onResume() 
    super.onResume();
    registerReceiver(mReceiver, mIntentFilter);


@Override
protected void onPause() 
    super.onPause();
    unregisterReceiver(mReceiver);


@Override
public boolean onCreateOptionsMenu(Menu menu) 
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;


private class WiFiDirectReceiver extends BroadcastReceiver 

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MainActivity mActivity;

    public WiFiDirectReceiver(WifiP2pManager manager, Channel channel, MainActivity activity) 
        super();
        mManager = manager;
        mChannel = channel;
        mActivity = 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) 
                   String title = "ANDROID_ID[" + getAndroid_ID() + "]";
                   title += "   MAC[" + getMACAddress() + "]";
                Toast.makeText(mActivity, "Wi-Fi Direct is enabled."+title, Toast.LENGTH_SHORT).show();
             else 
                Toast.makeText(mActivity, "Wi-Fi Direct is disabled.", Toast.LENGTH_SHORT).show();
            

         else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) 

            if (mManager != null) 
                mManager.requestPeers(mChannel, new PeerListListener() 

                    @Override
                    public void onPeersAvailable(WifiP2pDeviceList peers) 
                        if (peers != null) 
                            mDeviceList.addAll(peers.getDeviceList());
                            ArrayList<String> deviceNames = new ArrayList<String>();
                            for (WifiP2pDevice device : mDeviceList) 
                                deviceNames.add(device.deviceName);
                            
                            if (deviceNames.size() > 0) 
                                mAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_list_item_1, deviceNames);
                                if(flag==0)
                                
                                    flag=1;
                                showDeviceListDialog();
                                
                             else 
                                Toast.makeText(mActivity, "Device list is empty.", Toast.LENGTH_SHORT).show();
                            
                        
                    
                );
            

         else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) 


         else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) 

        
    



@Override
public void onClick(View v) 
    switch (v.getId()) 
    case R.id.discover:
        onDiscover();
        break;
    


private void onDiscover() 
    mManager.discoverPeers(mChannel, new ActionListener() 

        @Override
        public void onSuccess() 
            Toast.makeText(MainActivity.this, "Discover peers successfully.", Toast.LENGTH_SHORT).show();
        

        @Override
        public void onFailure(int reason) 
            Toast.makeText(MainActivity.this, "Discover peers failed.", Toast.LENGTH_SHORT).show();
        
    );


private void showDeviceListDialog() 
    DeviceListDialog deviceListDialog = new DeviceListDialog();
    deviceListDialog.show(getFragmentManager(), "devices");


private class DeviceListDialog extends DialogFragment 

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) 
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Select a device")
               .setSingleChoiceItems(mAdapter, 0, MainActivity.this)
               .setNegativeButton("Cancel", new DialogInterface.OnClickListener() 

                    @Override
                    public void onClick(DialogInterface dialog, int which) 
                        dialog.dismiss();
                    

               );

        return builder.create();
    



@Override
public void onClick(DialogInterface dialog, int which) 
    onDeviceSelected(which);
    dialog.dismiss();


private void onDeviceSelected(int which) 
    WifiP2pDevice device = mDeviceList.get(which);
    if (device == null) 
        return;
    

    WifiP2pConfig config = new WifiP2pConfig();
    config.deviceAddress = device.deviceAddress;
    mManager.connect(mChannel, config, new ActionListener() 

        @Override
        public void onSuccess() 
            Toast.makeText(MainActivity.this, "Connected", Toast.LENGTH_SHORT).show();
              Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
              intent.setType("image/*");
              startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE);
        

        @Override
        public void onFailure(int reason) 
            Toast.makeText(MainActivity.this, "Failed to connect", Toast.LENGTH_SHORT).show();
        
    );

/**
 * ANDROID_ID
 */
private String getAndroid_ID() 
    return Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);


/**
 * Wi-Fi MAC
 */
private String getMACAddress() 
    WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    WifiInfo wifiInfo = manager.getConnectionInfo();
    String mac = wifiInfo.getMacAddress();

    // After the group negotiation, we assign the group owner as the file
    // server. The file server is single threaded, single connection server
    // socket.

        new FileServerAsyncTask(getApplicationContext())
                .execute();





    return mac;

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) 

    // User has picked an image. Transfer it to group owner i.e peer using
    // FileTransferService.
    Uri uri = data.getData();

  Log.d("intent", "Intent----------- " + uri);
    Intent serviceIntent = new Intent(MainActivity.this, FileTransferService.class);
    serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE);
    serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());
    serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,
            getMACAddress());
    serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);
    startService(serviceIntent);

/**
 * A simple server socket that accepts connection and writes some data on
 * the stream.
 */
public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> 

    private Context context;


    /**
     * @param context
     * @param statusText
     */
    public FileServerAsyncTask(Context context) 
        this.context = context;

    

    @Override
    protected String doInBackground(Void... params) 
        try 
            System.out.println("insideeeeeeeeeeeeeeeeeeeeeeee");
            ServerSocket serverSocket = new ServerSocket(8988);
            Log.d("Server: Socket opened", "Server: Socket opened");
            Socket client = serverSocket.accept();
            Log.d("Server: connection done", "Server: connection done");
            final File f = new File(Environment.getExternalStorageDirectory() + "/"
                    + context.getPackageName() + "/wifip2pshared-" +   System.currentTimeMillis()
                    + ".jpg");

            File dirs = new File(f.getParent());
            if (!dirs.exists())
                dirs.mkdirs();
            f.createNewFile();

            Log.d("server: copying files ", "server: copying files " + f.toString());
            InputStream inputstream = client.getInputStream();
            copyFile(inputstream, new FileOutputStream(f));
            serverSocket.close();
            return f.getAbsolutePath();
         catch (IOException e) 
            Log.e("exp", e.getMessage());
            System.out.println(":iooo:"+e);
            return null;
        
    

    /*
     * (non-Javadoc)
     * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
     */
    @Override
    protected void onPostExecute(String result) 
        if (result != null) 

            Intent intent = new Intent();
            intent.setAction(android.content.Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.parse("file://" + result), "image/*");
            context.startActivity(intent);
        

    

    /*
     * (non-Javadoc)
     * @see android.os.AsyncTask#onPreExecute()
     */
    @Override
    protected void onPreExecute() 

    


public static boolean copyFile(InputStream inputStream, OutputStream out) 
    byte buf[] = new byte[1024];
    int len;
    long startTime=System.currentTimeMillis();

    try 
        while ((len = inputStream.read(buf)) != -1) 
            out.write(buf, 0, len);
        
        out.close();
        inputStream.close();
        long endTime=System.currentTimeMillis()-startTime;
        Log.v("","Time taken to transfer all bytes is : "+endTime);

     catch (IOException e) 
        Log.d("exp", e.toString());
        return false;
    
    return true;


@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) 
    // TODO Auto-generated method stub

    Toast.makeText(getApplicationContext(), "connectioninfoo", 3000).show();




清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jing.app.directwifi"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="14"
    android:targetSdkVersion="16" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Market filtering -->
<uses-feature
    android:name="android.hardware.wifi.direct"
    android:required="true" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="jing.app.directwifi.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <!-- Used for transferring files  after a successful connection -->
    <service
        android:name=".FileTransferService"
        android:enabled="true" />
</application>

</manifest>

这些是我的代码中使用的文件。形成我发现的教程

新的 FileServerAsyncTask(getApplicationContext()) .execute();

传入数据开始接受的地方,所以当我需要执行这个异步线程时,任何人都可以帮助我在哪个部分犯了错误。如何在设备之间传输文件。

【问题讨论】:

如果您在 wi-fi direct 示例中使用完全相同的代码,则整个程序的任何过程都不会有任何问题。不过,我想说你最好调试你的代码,特别是你的 copyFile 方法。 【参考方案1】:

演示 wifi direct 项目中的 android 代码能够传递文件是单向的。 IE。您只能将文件从客户端发送到服务器。您需要进一步修改以使其双向工作。

编辑 2:

为此,您需要知道两个已连接对等方的 IP 地址。在适当的地方使用我根据各种来源所做的修改并使用以下功能。

    public static String getIpAddress() 
    try 
        List<NetworkInterface> interfaces = Collections
                .list(NetworkInterface.getNetworkInterfaces());
        /*
         * for (NetworkInterface networkInterface : interfaces)  Log.v(TAG,
         * "interface name " + networkInterface.getName() + "mac = " +
         * getMACAddress(networkInterface.getName())); 
         */

        for (NetworkInterface intf : interfaces) 
            if (!getMACAddress(intf.getName()).equalsIgnoreCase(
                    Globals.thisDeviceAddress)) 
                // Log.v(TAG, "ignore the interface " + intf.getName());
                // continue;
            
            if (!intf.getName().contains("p2p"))
                continue;

            Log.v(TAG,
                    intf.getName() + "   " + getMACAddress(intf.getName()));

            List<InetAddress> addrs = Collections.list(intf
                    .getInetAddresses());

            for (InetAddress addr : addrs) 
                // Log.v(TAG, "inside");

                if (!addr.isLoopbackAddress()) 
                    // Log.v(TAG, "isnt loopback");
                    String sAddr = addr.getHostAddress().toUpperCase();
                    Log.v(TAG, "ip=" + sAddr);

                    boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);

                    if (isIPv4) 
                        if (sAddr.contains("192.168.49.")) 
                            Log.v(TAG, "ip = " + sAddr);
                            return sAddr;
                        
                    

                

            
        

     catch (Exception ex) 
        Log.v(TAG, "error in parsing");
     // for now eat exceptions
    Log.v(TAG, "returning empty ip address");
    return "";


public static String getMACAddress(String interfaceName) 
        try 
            List<NetworkInterface> interfaces = Collections
                    .list(NetworkInterface.getNetworkInterfaces());

            for (NetworkInterface intf : interfaces) 
                if (interfaceName != null) 
                    if (!intf.getName().equalsIgnoreCase(interfaceName))
                        continue;
                
                byte[] mac = intf.getHardwareAddress();
                if (mac == null)
                    return "";
                StringBuilder buf = new StringBuilder();
                for (int idx = 0; idx < mac.length; idx++)
                    buf.append(String.format("%02X:", mac[idx]));
                if (buf.length() > 0)
                    buf.deleteCharAt(buf.length() - 1);
                return buf.toString();
            
         catch (Exception ex) 
         // for now eat exceptions
        return "";
        /*
         * try  // this is so Linux hack return
         * loadFileAsString("/sys/class/net/" +interfaceName +
         * "/address").toUpperCase().trim();  catch (IOException ex)  return
         * null; 
         */
    

【讨论】:

我无法双向发送图像。实际上,我无法获取客户端的 IP 地址,即非 groupownder peer。 哪里有一个名为 getMACAddress..?? 的方法 什么是 Globals.thisDeviceAddress ?我收到一条错误消息,无法将全局变量解析为变量。为什么你把它设为 Globals ?为什么不使用 device.deviceAddress? 你也可以使用它。当前函数没有设备作为参数,因此我使用了它。【参考方案2】:

我认为您初始化客户端的方式会导致问题。

你需要调用它

new FileServerAsyncTask(getApplicationContext()).execute();

连接建立后检查是否是客户端。

现在,只有当它是服务器(组所有者)并且总是在您调用以获取 MAC 地址时,您才创建客户端套接字。

所以在客户端,您没有初始化套接字,因此无法接收文件。

查看 SDK 中提供的 WifiDirect 示例演示后,您可能会有所了解。

【讨论】:

以上是关于使用 wifi 直接在 Android 设备之间传输数据?的主要内容,如果未能解决你的问题,请参考以下文章

同一路由器上的Android wifi设备通信

Android Wifi 直接多连接 ad-hoc

android wifi直接直播摄像头视频流

如何通过 Android 设备创建的 WiFi 直接网络在 iOS 中使用 ZeroMQ 发送/接收消息

在 wifi 网络内的两个以上 Android 设备之间传输数据

Android:如何获取当前设备 WiFi 直接名称