无网络传输android

郭霖

Android技术分享平台,每天都有优质技术文章推送.你还可以向公众号投稿,将自己总结的技术心得分享给大家....

点对点文件传输,Android无网络传输文件的原理



今日科技快讯


昨日,上海交通执法部门在虹桥机场、虹桥火车站等共14个执法点开展了代号为“天网2号”的网约车非法客运专项整治行动。当天行动累计查处利用网约平台从事非法客运的案件37件,其中“滴滴”29件,“美团”6件,“神州”1件,“嘀嗒”1件。这些车辆及驾驶员均无营运资质,他们通过自己在网约平台软件上的注册账号接单,将乘客送至目的地,并收取车费。


作者简介


明天就是清明小长假了,希望大家都能好好享受这几天的假期时光,我们清明节后见!

本篇来自  叶应是叶 的投稿,分享了Android 实现无网络传输文件,一起来看看!希望大家喜欢。

 叶应是叶  的博客地址:

https://www.jianshu.com/u/9df45b87cfdf


正文


最近的项目需要实现一个Android手机之间无网络传输文件的功能,就发现了Wifi P2P(Wifi点对点)这么一个功能,最后也实现了通过Wifi 隔空传输文件的功能,这里我也来整理下代码,分享给大家。

Wifi P2P是在Android 4.0以及更高版本系统中加入的功能,通过Wifi P2P可以在不连接网络的情况下,直接与配对的设备进行数据交换。相对于蓝牙,Wifi P2P的搜索速度和传输速度更快,传输距离更远

实现的效果如下所示:

一般而言,开发步骤分为以下几点:

  • 在AndroidManifest中声明相关权限(网络和文件读写权限)

  • 获取WifiP2pManager,注册相关广播监听Wifi直连的状态变化

  • 指定某一台设备为服务器(用来接收文件),创建群组并作为群主存在,在指定端口监听客户端的连接请求,等待客户端发起连接请求以及文件传输请求

  • 客户端(用来发送文件)主动搜索附近的设备,加入到服务器创建的群组,获取服务器的IP地址,向其发起文件传输请求

  • 校验文件完整性

声明权限

Wifi P2P技术并不访问网络,但由于会使用到Java套接字,所以需要申请网络权限。此外,由于是要实现文件互传,所以也需要申请SD卡读写权限。

<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.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

注册广播

与Wifi P2P相关的广播有以下几个:

  1. WIFI_P2P_STATE_CHANGED_ACTION(用于指示Wifi P2P是否可用)

  2. WIFI_P2P_PEERS_CHANGED_ACTION(对等节点列表发生了变化)

  3. WIFI_P2P_CONNECTION_CHANGED_ACTION(Wifi P2P的连接状态发生了改变)

  4. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION(本设备的设备信息发生了变化)

当接收到这几个广播时,我们都需要到WifiP2pManager(对等网络管理器)来进行相应的信息请求,此外还需要用到Channel对象作为请求参数

mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mWifiP2pManager.initialize(this, getMainLooper(), this);

当收到WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION广播时,可以判断当前Wifi P2P是否可用

int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
   mDirectActionListener.wifiP2pEnabled(true);
} else {
   mDirectActionListener.wifiP2pEnabled(false);                
}

当收到WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION广播时,意味设备周围的可用设备列表发生了变化,可以通过requestPeers方法得到可用的设备列表,之后就可以选择当中的某一个设备进行连接操作

mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
   @Override
   public void onPeersAvailable(WifiP2pDeviceList peers) {
       mDirectActionListener.onPeersAvailable(peers.getDeviceList());
   }
});

当收到WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION广播时,意味着Wifi P2P的连接状态发生了变化,可能是连接到了某设备,或者是与某设备断开了连接

NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
   mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
       @Override
       public void onConnectionInfoAvailable(WifiP2pInfo info) {
           mDirectActionListener.onConnectionInfoAvailable(info);
       }
   });
   Log.e(TAG, "已连接p2p设备");
} else {
   mDirectActionListener.onDisconnection();
   Log.e(TAG, "与p2p设备已断开连接");
}

如果是与某设备连接上了,则可以通过requestConnectionInfo方法获取到连接信息。

当收到WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION广播时,则可以获取到本设备变化后的设备信息

(WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)

可以看出Wifi P2P的接口高度异步化,到现在已经用到了三个系统的回调函数,一个用于WifiP2pManager的初始化,两个用于在广播中异步请求数据,为了简化操作,此处统一使用一个自定义的回调函数,方法含义与系统的回调函数一致

public interface DirectActionListener extends WifiP2pManager.ChannelListener {

   void wifiP2pEnabled(boolean enabled);

   void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo);

   void onDisconnection();

   void onSelfDeviceAvailable(WifiP2pDevice wifiP2pDevice);

   void onPeersAvailable(Collection<WifiP2pDevice> wifiP2pDeviceList);

}

所以,整个广播接收器使用到的所有代码是:

/**
* 作者:chenZY
* 时间:2018/2/9 17:53
* 描述:
*/

public class DirectBroadcastReceiver extends BroadcastReceiver {

   private static final String TAG = "DirectBroadcastReceiver";

   private WifiP2pManager mWifiP2pManager;

   private WifiP2pManager.Channel mChannel;

   private DirectActionListener mDirectActionListener;

   public DirectBroadcastReceiver(WifiP2pManager wifiP2pManager, WifiP2pManager.Channel channel, DirectActionListener directActionListener) {
       mWifiP2pManager = wifiP2pManager;
       mChannel = channel;
       mDirectActionListener = directActionListener;
   }

   public static IntentFilter getIntentFilter() {
       IntentFilter intentFilter = new IntentFilter();
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
       return intentFilter;
   }

   @Override
   public void onReceive(Context context, Intent intent) {
       Log.e(TAG, "接收到广播: " + intent.getAction());
       if (!TextUtils.isEmpty(intent.getAction())) {
           switch (intent.getAction()) {
               // 用于指示 Wifi P2P 是否可用
               case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION: {
                   int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
                   if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                       mDirectActionListener.wifiP2pEnabled(true);
                   } else {
                       mDirectActionListener.wifiP2pEnabled(false);
                       List<WifiP2pDevice> wifiP2pDeviceList = new ArrayList<>();
                       mDirectActionListener.onPeersAvailable(wifiP2pDeviceList);
                   }
                   break;
               }
               // 对等节点列表发生了变化
               case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION: {
                   mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
                       @Override
                       public void onPeersAvailable(WifiP2pDeviceList peers) {
                           mDirectActionListener.onPeersAvailable(peers.getDeviceList());
                       }
                   });
                   break;
               }
               // Wifi P2P 的连接状态发生了改变
               case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION: {
                   NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                   if (networkInfo.isConnected()) {
                       mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
                           @Override
                           public void onConnectionInfoAvailable(WifiP2pInfo info) {
                               mDirectActionListener.onConnectionInfoAvailable(info);
                           }
                       });
                       Log.e(TAG, "已连接p2p设备");
                   } else {
                       mDirectActionListener.onDisconnection();
                       Log.e(TAG, "与p2p设备已断开连接");
                   }
                   break;
               }
               //本设备的设备信息发生了变化
               case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION: {
                   mDirectActionListener.onSelfDeviceAvailable((WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
                   break;
               }
           }
       }
   }

}

服务器端创建群组

假设当设备甲搜索到了设备B,并与设备乙连接到了一起,此时系统会自动创建一个群组(集团)并随机指定一台设备为群主(GroupOwner)。此时,对于两台设备来说,群主的IP地址是可知的(系统回调函数中有提供),但客户端的IP地址需要再来通过其他方法来主动获取。例如,可以在设备连接成功后,客户端主动发起对服务器端的插座连接请求,服务器端在指定端口监听客户端的连接请求,当连接成功后,服务器端就可以获取到客户端的IP地址了。此处为了简化操作,直接指定某台设备作为服务器端(群主),即直接指定某台设备用来接收文件。因此,服务器端要主动创建群组,并等待客户端的连接。

wifiP2pManager.createGroup(channel, new WifiP2pManager.ActionListener() {
   @Override
   public void onSuccess() {
       Log.e(TAG, "createGroup onSuccess");
       dismissLoadingDialog();
       showToast("onSuccess");
   }

    @Override
    public void onFailure(int reason) {
       Log.e(TAG, "createGroup onFailure: " + reason);
       dismissLoadingDialog();
       showToast("onFailure");
   }
});

此处,使用IntentService在后台监听客户端的Socket连接请求,并通过输入输出流来传输文件。此处的代码比较简单,就是在指定端口一直堵塞监听客户端的连接请求,获取待传输的文件信息模型FileTransfer,之后就进行实际的数据传输

@Override
   protected void onHandleIntent(Intent intent) {
       clean();
       File file = null;
       try {
           serverSocket = new ServerSocket();
           serverSocket.setReuseAddress(true);
           serverSocket.bind(new InetSocketAddress(PORT));
           Socket client = serverSocket.accept();
           Log.e(TAG, "客户端IP地址 : " + client.getInetAddress().getHostAddress());
           inputStream = client.getInputStream();
           objectInputStream = new ObjectInputStream(inputStream);
           FileTransfer fileTransfer = (FileTransfer) objectInputStream.readObject();
           Log.e(TAG, "待接收的文件: " + fileTransfer);
           String name = new File(fileTransfer.getFilePath()).getName();
           //将文件存储至指定位置
           file = new File(Environment.getExternalStorageDirectory() + "/" + name);
           fileOutputStream = new FileOutputStream(file);
           byte buf[] = new byte[512];
           int len;
           long total = 0;
           int progress;
           while ((len = inputStream.read(buf)) != -1) {
               fileOutputStream.write(buf, 0, len);
               total += len;
               progress = (int) ((total * 100) / fileTransfer.getFileLength());
               Log.e(TAG, "文件接收进度: " + progress);
               if (progressChangListener != null) {
                   progressChangListener.onProgressChanged(fileTransfer, progress);
               }
           }
           serverSocket.close();
           inputStream.close();
           objectInputStream.close();
           fileOutputStream.close();
           serverSocket = null;
           inputStream = null;
           objectInputStream = null;
           fileOutputStream = null;
           Log.e(TAG, "文件接收成功,文件的MD5码是:" + Md5Util.getMd5(file));
       } catch (Exception e) {
           Log.e(TAG, "文件接收 Exception: " + e.getMessage());
       } finally {
           clean();
           if (progressChangListener != null) {
               progressChangListener.onTransferFinished(file);
           }
           //再次启动服务,等待客户端下次连接
           startService(new Intent(this, WifiServerService.class));
       }
   }

因为客户端可能会多次发起连接请求,所以当此处理文件传输完成后(不管成功或失败),都需要重新startService,让服务再次堵塞等待客户端的连接请求。

FileTransfer包含三个字段,MD5码值用于校验文件的完整性,fileLength是为了用于计算文件的传输进度。

public class FileTransfer implements Serializable {

   //文件路径
   private String filePath;

   //文件大小
   private long fileLength;

   //MD5码
   private String md5;

   ···

}

为了将文件传输进度发布到外部界面,所以除了需要启动服务外,界面还需要绑定服务,此处就需要用到一个更新文件传输状态的接口。

public interface OnProgressChangListener {

       //当传输进度发生变化时
       void onProgressChanged(FileTransfer fileTransfer, int progress);

       //当传输结束时
       void onTransferFinished(File file);

   }

因此,需要将progressChangListener作为参数传给WifiServerService,并在进度变化时更新进度对话框。

private WifiServerService.OnProgressChangListener progressChangListener = new WifiServerService.OnProgressChangListener() {
       @Override
       public void onProgressChanged(final FileTransfer fileTransfer, final int progress) {
           runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   progressDialog.setMessage("文件名: " + new File(fileTransfer.getFilePath()).getName());
                   progressDialog.setProgress(progress);
                   progressDialog.show();
               }
           });
       }

       @Override
       public void onTransferFinished(final File file) {
           runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   progressDialog.cancel();
                   if (file != null && file.exists()) {
                       openFile(file.getPath());
                   }
               }
           });
       }
   };

客户端加入群组并发起文件传输请求

文件发送界面SendFileActivity需要实现DirectActionListener接口首先,需要先注册P2P广播,以便获取周边设备信息以及连接状态

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_send_file);
       initView();
       mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
       mChannel = mWifiP2pManager.initialize(this, getMainLooper(), this);
       broadcastReceiver = new DirectBroadcastReceiver(mWifiP2pManager, mChannel, this);
       registerReceiver(broadcastReceiver, DirectBroadcastReceiver.getIntentFilter());
   }

通过discoverPeers方法搜索周边设备,回调函数用于通知方法是否调用成功。

mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
   @Override
   public void onSuccess() {
       showToast("Success");
   }

   @Override
   public void onFailure(int reasonCode) {
       showToast("Failure");
       loadingDialog.cancel();
    }
});

当搜索结束后,系统就会触发WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION广播,此时就可以调用requestPeers方法获取设备列表信息,此处用RecyclerView展示列表,在onPeersAvailable方法刷新列表。

mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
   @Override
   public void onPeersAvailable(WifiP2pDeviceList peers) {
       mDirectActionListener.onPeersAvailable(peers.getDeviceList());
   }
});
@Override
   public void onPeersAvailable(Collection<WifiP2pDevice> wifiP2pDeviceList) {
       Log.e(TAG, "onPeersAvailable :" + wifiP2pDeviceList.size());
       this.wifiP2pDeviceList.clear();
       this.wifiP2pDeviceList.addAll(wifiP2pDeviceList);
       deviceAdapter.notifyDataSetChanged();
       loadingDialog.cancel();
   }

之后,通过点击事件选中群主(服务器端)设备,通过连接方法请求与之进行连接

private void connect() {
   WifiP2pConfig config = new WifiP2pConfig();
   if (config.deviceAddress != null && mWifiP2pDevice != null) {
       config.deviceAddress = mWifiP2pDevice.deviceAddress;
       config.wps.setup = WpsInfo.PBC;
       showLoadingDialog("正在连接 " + mWifiP2pDevice.deviceName);
       mWifiP2pManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
           @Override
           public void onSuccess() {
               Log.e(TAG, "connect onSuccess");
           }

           @Override
           public void onFailure(int reason) {
               showToast("连接失败 " + reason);
               dismissLoadingDialog();
           }
       });
   }
}

此处依然无法通过函数函数来判断连接结果,需要依靠系统发出的WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION   方法来获取到连接结果,在此处可以通过requestConnectionInfo获取到组连接信息,信息最后通过onConnectionInfoAvailable方法传递出来,在此可以判断当前设备是否为群主,获取群组IP地址。

@Override
public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
   dismissLoadingDialog();
   wifiP2pDeviceList.clear();
   deviceAdapter.notifyDataSetChanged();
   btn_disconnect.setEnabled(true);
   btn_chooseFile.setEnabled(true);
   Log.e(TAG, "onConnectionInfoAvailable");
   Log.e(TAG, "onConnectionInfoAvailable groupFormed: " + wifiP2pInfo.groupFormed);
   Log.e(TAG, "onConnectionInfoAvailable isGroupOwner: " + wifiP2pInfo.isGroupOwner);
   Log.e(TAG, "onConnectionInfoAvailable getHostAddress: " + wifiP2pInfo.groupOwnerAddress.getHostAddress());
   StringBuilder stringBuilder = new StringBuilder();
   if (mWifiP2pDevice != null) {
       stringBuilder.append("连接的设备名:");
       stringBuilder.append(mWifiP2pDevice.deviceName);
       stringBuilder.append("\n");
       stringBuilder.append("连接的设备的地址:");
       stringBuilder.append(mWifiP2pDevice.deviceAddress);
   }
   stringBuilder.append("\n");
   stringBuilder.append("是否群主:");
   stringBuilder.append(wifiP2pInfo.isGroupOwner ? "是群主" : "非群主");
   stringBuilder.append("\n");
   stringBuilder.append("群主IP地址:");
   stringBuilder.append(wifiP2pInfo.groupOwnerAddress.getHostAddress());
   tv_status.setText(stringBuilder);
   if (wifiP2pInfo.groupFormed && !wifiP2pInfo.isGroupOwner) {
       this.wifiP2pInfo = wifiP2pInfo;
   }
}

至此服务器端和客户端已经通过Wifi P2P连接在了一起,客户端也获取到了服务器端的IP地址,在选取好待发送的文件后就可以主动发起对服务器端的连接请求了。

发起选取文件的方法

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, 1);

获取选取的文件的实际路径

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   if (requestCode == 1) {
       if (resultCode == RESULT_OK) {
           Uri uri = data.getData();
           if (uri != null) {
               String path = getPath(this, uri);
               if (path != null) {
                   File file = new File(path);
                   if (file.exists() && wifiP2pInfo != null) {
                       FileTransfer fileTransfer = new FileTransfer(file.getPath(), file.length());
                       Log.e(TAG, "待发送的文件:" + fileTransfer);
                       new WifiClientTask(this, fileTransfer).execute(wifiP2pInfo.groupOwnerAddress.getHostAddress());
                   }
               }
           }
       }
   }
}

private String getPath(Context context, Uri uri) {
   if ("content".equalsIgnoreCase(uri.getScheme())) {
       Cursor cursor = context.getContentResolver().query(uri, new String[]{"_data"}, null, null, null);
       if (cursor != null) {
          if (cursor.moveToFirst()) {
               String data = cursor.getString(cursor.getColumnIndex("_data"));
               cursor.close();
               return data;
           }
       }
   } else if ("file".equalsIgnoreCase(uri.getScheme())) {
       return uri.getPath();
   }
   return null;
}

文件的发送操作放到AsyncTask中处理,将服务器端的IP地址作为参数传进来,在正式发送文件前,先发送包含文件信息(文件名,文件大小,文件MD5码)的信息模型FileTransfer,并在发送文件的过程中同时更新进度

/**
* 作者:叶应是叶
* 时间:2018/2/15 8:51
* 描述:客户端发送文件
*/

public class WifiClientTask extends AsyncTask<String, Integer, Boolean> {

   private ProgressDialog progressDialog;

   private FileTransfer fileTransfer;

   private static final int PORT = 4786;

   private static final String TAG = "WifiClientTask";

   public WifiClientTask(Context context, FileTransfer fileTransfer) {
       this.fileTransfer = fileTransfer;
       progressDialog = new ProgressDialog(context);
       progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
       progressDialog.setCancelable(false);
       progressDialog.setCanceledOnTouchOutside(false);
       progressDialog.setTitle("正在发送文件");
       progressDialog.setMax(100);
   }

   @Override
   protected void onPreExecute() {
       progressDialog.show();
   }

   @Override
   protected Boolean doInBackground(String... strings) {
       fileTransfer.setMd5(Md5Util.getMd5(new File(fileTransfer.getFilePath())));
       Log.e(TAG, "文件的MD5码值是:" + fileTransfer.getMd5());
       Socket socket = null;
       OutputStream outputStream = null;
       ObjectOutputStream objectOutputStream = null;
       InputStream inputStream = null;
       try {
           socket = new Socket();
           socket.bind(null);
           socket.connect((new InetSocketAddress(strings[0], PORT)), 10000);
           outputStream = socket.getOutputStream();
           objectOutputStream = new ObjectOutputStream(outputStream);
           objectOutputStream.writeObject(fileTransfer);
           inputStream = new FileInputStream(new File(fileTransfer.getFilePath()));
           long fileSize = fileTransfer.getFileLength();
           long total = 0;
           byte buf[] = new byte[512];
           int len;
           while ((len = inputStream.read(buf)) != -1) {
               outputStream.write(buf, 0, len);
               total += len;
               int progress = (int) ((total * 100) / fileSize);
               publishProgress(progress);
               Log.e(TAG, "文件发送进度:" + progress);
           }
           outputStream.close();
           objectOutputStream.close();
           inputStream.close();
           socket.close();
           outputStream = null;
           objectOutputStream = null;
           inputStream = null;
           socket = null;
           Log.e(TAG, "文件发送成功");
           return true;
       } catch (Exception e) {
           Log.e(TAG, "文件发送异常 Exception: " + e.getMessage());
       } finally {
           if (outputStream != null) {
               try {
                   outputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
           if (objectOutputStream != null) {
               try {
                   objectOutputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
           if (inputStream != null) {
               try {
                   inputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
           if (socket != null) {
               try {
                   socket.close();
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       }
       return false;
   }

   @Override
   protected void onProgressUpdate(Integer... values) {
       progressDialog.setProgress(values[0]);
   }

   @Override
   protected void onPostExecute(Boolean aBoolean) {
       progressDialog.cancel();
       Log.e(TAG, "onPostExecute: " + aBoolean);
   }

}

校验文件完整性

传输文件的完整性主要是通过计算文件的MD5码值来保证了,在发送文件前,即在WifiClientTask的doInBackground方法中进行计算,将MD5码值赋给给FileTransfer模型,通过如下方法计算得到

/**
* 作者:叶应是叶
* 时间:2018/2/14 21:16
* 描述:
*/

public class Md5Util {

   public static String getMd5(File file) {
       InputStream inputStream = null;
       byte[] buffer = new byte[2048];
       int numRead;
       MessageDigest md5;
       try {
           inputStream = new FileInputStream(file);
           md5 = MessageDigest.getInstance("MD5");
           while ((numRead = inputStream.read(buffer)) > 0) {
               md5.update(buffer, 0, numRead);
           }
           inputStream.close();
           inputStream = null;
           return md5ToString(md5.digest());
       } catch (Exception e) {
           return null;
       } finally {
           if (inputStream != null) {
               try {
                   inputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }

   private static String md5ToString(byte[] md5Bytes) {
       StringBuilder hexValue = new StringBuilder();
       for (byte b : md5Bytes) {
           int val = ((int) b) & 0xff;
           if (val < 16) {
               hexValue.append("0");
           }
           hexValue.append(Integer.toHexString(val));
       }
       return hexValue.toString();
   }

}


总结


因为客户端会将FileTransfer传给服务器端,所以服务器端在文件传输结束后,可以重新计算文件的MD5码值,进行对比以判断文件是否完整。这里分享下上述代码,地址如下:

https://github.com/leavesC/WifiP2P


欢迎长按下图 -> 识别图中二维码

或者 扫一扫 关注我的公众号

android socket 点对点文件传输mode

2015年05月05日 49KB 下载

您有一张免费的北京车展门票等待领取美好购车节 · 顶新
2018北京高端会所体验!揭开高级会所神秘面纱!花韵会所 · 顶新
java socket实现的点对点文件传输

2009年08月12日 12KB 下载

Qt 实现的简单网络文件传输

2014年06月11日 11KB 下载

MFC Csocket实现点对点文件传输

2009年05月11日 1.92MB 下载

个人资料

原创
413
粉丝
214
喜欢
55
评论
41
等级:
访问:
10万+
积分:
4693
排名:
8037
勋章:

最新评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值