1. 程式人生 > >Android連線熱點的Socket檔案傳輸

Android連線熱點的Socket檔案傳輸

最近把測試丟過來的種種BUG解決後,終於有時間去研究研究Socket通訊,再加上以前做的WiFi連線和熱點開啟,於是有了現在的這篇博文:建立熱點發送檔案,讓另一臺手機連線熱點接收檔案。

效果圖:

兩臺裝置是如何傳輸檔案的:

  • 傳送端->建立WiFi熱點
  • 接收端->連線熱點
  • 傳送端->傳送檔案列表
  • 接收端->收到後展示檔案列表,選擇要接收的檔案傳送給傳送端
  • 傳送端->傳送所選檔案
  • 接收端->開始接收…

傳送端->建立WiFi熱點:

由於Android沒有直接開啟熱點的API,所以我們這裡採用反射。

/**
 * 開啟便攜熱點
 * @param
context 上下文 * @param SSID 便攜熱點SSID * @param password 便攜熱點密碼 * @return */
public static boolean openAp(Context context, String SSID, String password) { if(TextUtils.isEmpty(SSID)) { return false; } WifiManager wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE); if
(wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(false); } WifiConfiguration wifiConfiguration = getApConfig(SSID, password); try { if(wifiManager.isWifiEnabled()) { //關閉WiFi wifiManager.setWifiEnabled(false); } if(isApOn(context)) { //關閉熱點
closeAp(context); } //使用反射開啟Wi-Fi熱點 Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); method.invoke(wifiManager, wifiConfiguration, true); return true; } catch (Exception e) { e.printStackTrace(); } return false; }

接收端->連線熱點:

連線熱點前先開啟WiFi廣播監聽事件,然後開啟WiFi,掃描周圍可用WiFi列表,拿到SSID再進行連線,最後在WiFi廣播監聽事件中比較已連線WIFI的SSID是否正確。

WiFi廣播監聽事件:

/**
 * 註冊監聽WiFi操作的系統廣播
 */
private void registerWifiReceiver() {
    IntentFilter filter = new IntentFilter();
    //監聽WiFi開啟與關閉
    filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    //監聽掃描周圍可用WiFi列表結果
    filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    //監聽WiFi連線與斷開
    filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    //註冊廣播
    registerReceiver(mWifiBroadcaseReceiver, filter);
}
/**
 * 反註冊WiFi相關的系統廣播
 */
private void unregisterWifiReceiver() {
    if (mWifiBroadcaseReceiver != null) {
        unregisterReceiver(mWifiBroadcaseReceiver);
        mWifiBroadcaseReceiver = null;
    }
}

WiFi廣播接收器:

public abstract class WifiBroadcaseReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null) {
            if(intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                //監聽WiFi開啟/關閉事件
                int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
                if(wifiState == WifiManager.WIFI_STATE_ENABLED) {
                    //WiFi已開啟
                    onWifiEnabled();
                } else if(wifiState == WifiManager.WIFI_STATE_DISABLED) {
                    //WiFi已關閉
                    onWifiDisabled();
                }
            } else if(intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                WifiMgr wifiMgr = new WifiMgr(context);
                List<ScanResult> scanResults = wifiMgr.getScanResults();
                if(wifiMgr.isWifiEnabled() && scanResults != null && scanResults.size() > 0) {
                    //成功掃描
                    onScanResultsAvailable(scanResults);
                }
            } else if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                //網路狀態改變的廣播
                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                if (info != null) {
                    if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
                        //WiFi已連線
                        WifiMgr wifiMgr = new WifiMgr(context);
                        String connectedSSID = wifiMgr.getConnectedSSID();
                        onWifiConnected(connectedSSID);
                    } else if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
                        //WiFi已斷開連線
                        onWifiDisconnected();
                    }
                }
            }
        }
    }

    public abstract void onWifiEnabled();

    public abstract void onWifiDisabled();

    public abstract void onScanResultsAvailable(List<ScanResult> scanResults);

    public abstract void onWifiConnected(String connectedSSID);

    public abstract void onWifiDisconnected();
}

開啟和關閉WiFi:

mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

/**
 * 開啟WiFi
 */
public void openWifi() {
    if (!mWifiManager.isWifiEnabled()) {
        mWifiManager.setWifiEnabled(true);
    }
}
/**
 * 關閉WiFi
 */
public void closeWifi() {
    if (mWifiManager.isWifiEnabled()) {
        mWifiManager.setWifiEnabled(false);
    }

/**
 * 當前WiFi是否開啟
 */
public boolean isWifiEnabled() {
    return mWifiManager.isWifiEnabled();
}

掃描周圍可用WiFi列表:

/**
 * 掃描周圍可用WiFi
 * @return
 */
public boolean startScan() {
    if(isWifiEnabled()) {
        return mWifiManager.startScan();
    }
    return false;
}

拿到WiFi掃描結果並且連線熱點,當接收端連線成功後,會發一個UDP廣播告知區域網內裝置連線熱點成功:

/**
 * WiFi廣播接收器
 */
private WifiBroadcaseReceiver mWifiBroadcaseReceiver = new WifiBroadcaseReceiver() {
    @Override
    public void onWifiEnabled() {
        //WiFi已開啟,開始掃描可用WiFi
        mWifiMgr.startScan();
    }
    @Override
    public void onWifiDisabled() {
        //WiFi已關閉,清除可用WiFi列表
        mSelectedSSID = "";
        mScanResults.clear();
        setupWifiAdapter();
    }
    @Override
    public void onScanResultsAvailable(List<ScanResult> scanResults) {
        //掃描周圍可用WiFi成功,設定可用WiFi列表
        mScanResults.clear();
        mScanResults.addAll(scanResults);
        setupWifiAdapter();
    }
    @Override
    public void onWifiConnected(String connectedSSID) {
        //判斷指定WiFi是否連線成功
        if (connectedSSID.equals(mSelectedSSID) && !mIsSendInitOrder) {
            //連線成功
            //告知傳送端,接收端初始化完畢
            mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
            mIsSendInitOrder = true;
        } else {
              //連線成功的不是裝置WiFi,清除該WiFi,重新掃描周圍WiFi
              LogUtils.e("連線到錯誤WiFi,正在斷開重連...");
              mWifiMgr.disconnectWifi(connectedSSID);
              mWifiMgr.startScan();
        }
    }
    @Override
    public void onWifiDisconnected() {
    }
};

傳送端->傳送檔案列表:

當傳送端收到初始化完畢指令時,將用UDP廣播發送檔案列表。

/**
 * 等待接收端傳送初始化完成指令,向其傳送檔案列表
 * @param serverPort
 * @throws Exception
 */
private void receiveInitSuccessOrder(int serverPort) throws Exception {
    //確保WiFi連線後獲取正確IP地址
    int tryCount = 0;
    String localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
    while (localIpAddress.equals(Consts.DEFAULT_UNKNOW_IP) && tryCount < Consts.DEFAULT_TRY_COUNT) {
        Thread.sleep(1000);
        localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
        tryCount ++;
    }
    /** 這裡使用UDP傳送和接收指令 */
    mDatagramSocket = new DatagramSocket(serverPort);
    while (true) {
        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
        mDatagramSocket.receive(receivePacket);
        String response = new String(receivePacket.getData()).trim();
        if(isNotEmptyString(response)) {
            LogUtils.e("接收到的訊息 -------->>>" + response);
            if(response.equals(Consts.MSG_FILE_RECEIVER_INIT_SUCCESS)) {
                //初始化成功指令
                mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
                //傳送檔案列表
                InetAddress inetAddress = receivePacket.getAddress();
                int port = receivePacket.getPort();
                //通過UDP傳送檔案列表給接收端
                sendFileInfoListToFileReceiverWithUdp(inetAddress, port);
            } else if(response.equals(Consts.MSG_START_SEND)) {
                //開始傳送指令
                initSenderServer();
            } else {
                //接收端發來的待發送檔案列表
                parseFileInfo(response);
            }
        }
    }
}

/**
 * 通過UDP傳送檔案列表給接收端
 * @param ipAddress IP地址
 * @param serverPort 埠號
 */
private void sendFileInfoListToFileReceiverWithUdp(InetAddress ipAddress, int serverPort) {
    if(!isEmptyList(mAllFileInfos)) {
        String jsonStr = FileInfo.toJsonStr(mAllFileInfos);
        DatagramPacket sendFileInfoPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, serverPort);
        try {
            //傳送檔案列表
            mDatagramSocket.send(sendFileInfoPacket);
            LogUtils.i("傳送訊息 --------->>>" + jsonStr + "=== Success!");
            mHandler.obtainMessage(MSG_SET_STATUS, "成功傳送檔案列表...").sendToTarget();
        } catch (IOException e) {
            e.printStackTrace();
            LogUtils.i("傳送訊息 --------->>>" + jsonStr + "=== 失敗!");
        }
    }
}

接收端->選擇檔案,並告知傳送端開始傳送:

收到檔案列表後,接收端會將檔案列表展示在RecyclerView控制元件,通過選擇需要接收的檔案,點選“開始傳送”按鈕,將傳送“開始傳送”指令到傳送端,開啟埠進行檔案接收。

/**
 * 設定接收檔案列表介面卡
 */
private void setupReceiveFilesAdapter() {
    List<Map.Entry<String, FileInfo>> fileInfos = AppContext.getAppContext().getReceiverFileInfoMap();
    Collections.sort(fileInfos, Consts.DEFAULT_COMPARATOR);
    //設定介面卡
    mReceiveFilesAdapter = new CommonAdapter<Map.Entry<String, FileInfo>>(getContext(), R.layout.item_files_selector, fileInfos) {
        @Override
        protected void convert(ViewHolder holder, Map.Entry<String, FileInfo> fileInfoMap, int position) {
            final FileInfo fileInfo = fileInfoMap.getValue();
            //檔案路徑
            holder.setText(R.id.tv_item_files_selector_file_path, fileInfo.getFilePath());
            //檔案大小
            holder.setText(R.id.tv_item_files_selector_size, FileUtils.FormetFileSize(fileInfo.getSize()));
            //檔案接收狀態
            if(fileInfo.getProgress() >= 100) {
                holder.setText(R.id.tv_item_files_selector_status, "接收完畢");
            } else if(fileInfo.getProgress() == 0) {
                holder.setText(R.id.tv_item_files_selector_status, "準備接收");
            } else if(fileInfo.getProgress() < 100) {
                holder.setText(R.id.tv_item_files_selector_status, "正在接收");
            } else {
                holder.setText(R.id.tv_item_files_selector_status, "接收失敗");
            }
            //檔案接收進度
            ProgressBar progressBar = holder.getView(R.id.pb_item_files_selector);
            progressBar.setProgress(fileInfo.getProgress());
            //選中檔案
            CheckBox checkBox = holder.getView(R.id.cb_item_files_selector);
            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if(isChecked) {
                        mSendFileInfos.add(fileInfo);
                    } else {
                        mSendFileInfos.remove(fileInfo);
                    }
                    //選中的檔案個數大於零才可點選底部按鈕
                    btnSendFileList.setEnabled(mSendFileInfos.size() > 0);
                }
            });
        }
    };
    mReceiveFilesRecyclerView.setAdapter(mReceiveFilesAdapter);
    //設定ListView樣式
    mReceiveFilesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    //分割線
    mReceiveFilesRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
}

/**
 * 傳送選中的檔案列表給傳送端
 */
private void sendFileListToFileSender() {
    new Thread() {
        @Override
        public void run() {
            try {
                //獲取IP地址
                String serverIp = mWifiMgr.getIpAddressFromHotspot();
                if(mDatagramSocket == null) {
                    //解決:java.net.BindException: bind failed: EADDRINUSE (Address already in use)
                    mDatagramSocket = new DatagramSocket(null);
                    mDatagramSocket.setReuseAddress(true);
                    mDatagramSocket.bind(new InetSocketAddress(Consts.DEFAULT_SERVER_UDP_PORT));
                }
                //傳送選中的檔案列表
                InetAddress ipAddress = InetAddress.getByName(serverIp);
                String jsonStr = FileInfo.toJsonStr(mSendFileInfos);
                DatagramPacket sendPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
                mDatagramSocket.send(sendPacket);
                LogUtils.i("傳送訊息 ------->>>" + jsonStr);
                //傳送開始傳送檔案指令
                byte[] sendData = Consts.MSG_START_SEND.getBytes(BaseTransfer.UTF_8);
                DatagramPacket sendPacket2 = new DatagramPacket(sendData, sendData.length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
                mDatagramSocket.send(sendPacket2);
                LogUtils.i("傳送訊息 ------->>>" + sendData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }.start();
}

/**
 * ServerSocket啟動執行緒
 */
private class ReceiveServerRunnable implements Runnable {
    @Override
    public void run() {
        try {
            //開始接收檔案
            String serverIp = mWifiMgr.getIpAddressFromHotspot();
            List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getReceiverFileInfoMap();
            Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
            for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
                //連線傳送端,逐個檔案進行接收
                final int position = fileInfoList.indexOf(fileInfoMap);
                mClientSocket = new Socket(serverIp, Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
                FileReceiver fileReceiver = new FileReceiver(mClientSocket, fileInfoMap.getValue());
                fileReceiver.setOnReceiveListener(new FileReceiver.OnReceiveListener() {
                    @Override
                    public void onStart() {
                        mHandler.obtainMessage(MSG_SET_STATUS, "開始接收"+ FileUtils.getFileName(fileInfoMap.getValue().getFilePath())).sendToTarget();
                    }
                    @Override
                    public void onProgress(FileInfo fileInfo, long progress, long total) {
                        //更新接收進度檢視
                        int i_progress = (int) (progress * 100 / total);
                        LogUtils.e("正在接收:" + fileInfo.getFilePath() + "\n當前進度:" + i_progress);
                        Message msg = new Message();
                        msg.what = MSG_UPDATE_PROGRESS;
                        msg.arg1 = position;
                        msg.arg2 = i_progress;
                        mHandler.sendMessage(msg);
                    }
                    @Override
                    public void onSuccess(FileInfo fileInfo) {
                        //接收成功
                        mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收成功").sendToTarget();
                        fileInfo.setResult(FileInfo.FLAG_SUCCESS);
                        AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
                        Message msg = new Message();
                        msg.what = MSG_UPDATE_PROGRESS;
                        msg.arg1 = position;
                        msg.arg2 = 100;
                        mHandler.sendMessage(msg);
                    }
                    @Override
                    public void onFailure(Throwable throwable, FileInfo fileInfo) {
                        if(fileInfo != null) {
                            //接收失敗
                            mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收失敗").sendToTarget();
                            fileInfo.setResult(FileInfo.FLAG_FAILURE);
                            AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = position;
                            msg.arg2 = -1;
                            mHandler.sendMessage(msg);
                        }
                    }
                });
                //加入執行緒池執行
                mFileReceiverList.add(fileReceiver);
                AppContext.getAppContext().MAIN_EXECUTOR.execute(fileReceiver);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

檔案接收執行緒:

public class FileReceiver extends BaseTransfer implements Runnable {

    /**
     * 接收檔案的Socket的輸入輸出流
     */
    private Socket mSocket;
    private InputStream mInputStream;

    /**
     * 待接收的檔案資料
     */
    private FileInfo mFileInfo;

    /**
     * 用來控制執行緒暫停、恢復
     */
    private final Object LOCK = new Object();
    private boolean mIsPaused = false;

    /**
     * 設定未執行執行緒的不執行標識
     */
    private boolean mIsStop;

    /**
     * 該執行緒是否執行完畢
     */
    private boolean mIsFinish;

    /**
     * 檔案接收監聽事件
     */
    private OnReceiveListener mOnReceiveListener;


    public FileReceiver(Socket socket, FileInfo fileInfo) {
        mSocket = socket;
        mFileInfo = fileInfo;
    }

    /**
     * 設定接收監聽事件
     * @param onReceiveListener
     */
    public void setOnReceiveListener(OnReceiveListener onReceiveListener) {
        mOnReceiveListener = onReceiveListener;
    }

    @Override
    public void run() {
        if(mIsStop) {
            return;
        }

        //初始化
        try {
            if(mOnReceiveListener != null) {
                mOnReceiveListener.onStart();
            }
            init();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileReceiver init() ------->>> occur expection");
            if(mOnReceiveListener != null) {
                mOnReceiveListener.onFailure(e, mFileInfo);
            }
        }

        //傳送檔案實體資料
        try {
            parseBody();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileReceiver parseBody() ------->>> occur expection");
            if(mOnReceiveListener != null) {
                mOnReceiveListener.onFailure(e, mFileInfo);
            }
        }

        //檔案傳輸完畢
        try {
            finishTransfer();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileReceiver finishTransfer() ------->>> occur expection");
            if(mOnReceiveListener != null) {
                mOnReceiveListener.onFailure(e, mFileInfo);
            }
        }
    }

    @Override
    public void init() throws Exception {
        if(mSocket != null) {
            mInputStream = mSocket.getInputStream();
        }
    }

    @Override
    public void parseBody() throws Exception {
        if(mFileInfo == null) {
            return;
        }

        long fileSize = mFileInfo.getSize();
        OutputStream fos = new FileOutputStream(FileUtils.gerateLocalFile(mFileInfo.getFilePath()));

        byte[] bytes = new byte[BYTE_SIZE_DATA];
        long total = 0;
        int len = 0;

        long sTime = System.currentTimeMillis();
        long eTime = 0;
        while ((len = mInputStream.read(bytes)) != -1) {
            synchronized (LOCK) {
                if(mIsPaused) {
                    try {
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //寫入檔案
                fos.write(bytes, 0, len);
                total = total + len;

                //每隔200毫秒返回一次進度
                eTime = System.currentTimeMillis();
                if(eTime - sTime > 200) {
                    sTime = eTime;
                    if(mOnReceiveListener != null) {
                        mOnReceiveListener.onProgress(mFileInfo, total, fileSize);
                    }
                }
            }
        }

        //檔案接收成功
        if(mOnReceiveListener != null) {
            mOnReceiveListener.onSuccess(mFileInfo);
        }
        mIsFinish = true;
    }

    @Override
    public void finishTransfer() throws Exception {
        if(mInputStream != null) {
            try {
                mInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(mSocket != null && mSocket.isConnected()) {
            try {
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 暫停接收執行緒
     */
    public void pause() {
        synchronized (LOCK) {
            mIsPaused = true;
            LOCK.notifyAll();
        }
    }

    /**
     * 恢復接收執行緒
     */
    public void resume() {
        synchronized (LOCK) {
            mIsPaused = false;
            LOCK.notifyAll();
        }
    }

    /**
     * 設定當前的接收任務不執行
     */
    public void stop() {
        mIsStop = true;
    }

    /**
     * 檔案是否在接收中
     * @return
     */
    public boolean isRunning() {
        return !mIsFinish;
    }

    /**
     * 檔案接收監聽事件
     */
    public interface OnReceiveListener {
        void onStart();
        void onProgress(FileInfo fileInfo, long progress, long total);
        void onSuccess(FileInfo fileInfo);
        void onFailure(Throwable throwable, FileInfo fileInfo);
    }
}

傳送端->傳送所選檔案:

收到接收端發來的檔案列表和“開始傳送”指令後,傳送端將會把所選檔案逐個傳送給接收端。

/**
 * 初始化傳送端服務,開始傳送檔案
 */
private void initSenderServer() {
    mSenderServerRunnable = new SenderServerRunnable();
    new Thread(mSenderServerRunnable).start();
}

/**
 * 檔案傳送執行緒
 */
private class SenderServerRunnable implements Runnable {
    private ServerSocket mServerSocket;
    @Override
    public void run() {
        try {
            //獲取待發送的檔案列表資料,按position索引排序
            List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getSendFileInfoMap();
            Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
            mServerSocket = new ServerSocket(Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
            //逐個檔案進行傳送
            for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
                final FileInfo fileInfo = fileInfoMap.getValue();
                Socket socket = mServerSocket.accept();
                FileSender fileSender = new FileSender(socket, fileInfo);
                fileSender.setOnSendListener(new FileSender.OnSendListener() {
                    @Override
                    public void onStart() {
                        mHandler.obtainMessage(MSG_SET_STATUS, "開始傳送"+ FileUtils.getFileName(fileInfo.getFilePath())).sendToTarget();
                    }
                    @Override
                    public void onProgress(long progress, long total) {
                        //更新發送進度檢視
                        int i_progress = (int) (progress * 100 / total);
                        LogUtils.e("正在傳送:" + fileInfo.getFilePath() + "\n當前進度:" + i_progress);
                        Message msg = new Message();
                        msg.what = MSG_UPDATE_PROGRESS;
                        msg.arg1 = fileInfo.getPosition();
                        msg.arg2 = i_progress;
                        mHandler.sendMessage(msg);
                    }
                    @Override
                    public void onSuccess(FileInfo fileInfo) {
                        //傳送成功
                        mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "傳送成功").sendToTarget();
                        fileInfo.setResult(FileInfo.FLAG_SUCCESS);
                        AppContext.getAppContext().updateSendFileInfo(fileInfo);
                        Message msg = new Message();
                        msg.what = MSG_UPDATE_PROGRESS;
                        msg.arg1 = fileInfo.getPosition();
                        msg.arg2 = 100;
                        mHandler.sendMessage(msg);
                    }
                    @Override
                    public void onFailure(Throwable throwable, FileInfo fileInfo) {
                        //傳送失敗
                        mHandler.obtainMessage(MSG_SET_STATUS, "檔案:" + FileUtils.getFileName(fileInfo.getFilePath()) + "傳送失敗").sendToTarget();
                        fileInfo.setResult(FileInfo.FLAG_FAILURE);
                        AppContext.getAppContext().updateSendFileInfo(fileInfo);
                        Message msg = new Message();
                        msg.what = MSG_UPDATE_PROGRESS;
                        msg.arg1 = fileInfo.getPosition();
                        msg.arg2 = -1;
                        mHandler.sendMessage(msg);
                    }
                });
                //新增到執行緒池執行
                mFileSenderList.add(fileSender);
                AppContext.FILE_SENDER_EXECUTOR.execute(fileSender);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 關閉Socket連線
     */
    public void closeServerSocket() {
        if(mServerSocket != null) {
            try {
                mServerSocket.close();
                mServerSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

檔案傳送執行緒:

public class FileSender extends BaseTransfer implements Runnable {

    /**
     * 待發送的檔案資料
     */
    private FileInfo mFileInfo;

    /**
     * 傳送檔案的Socket輸入輸出流
     */
    private Socket mSocket;
    private OutputStream mOutputStream;

    /**
     * 用來控制執行緒暫停、恢復
     */
    private final Object LOCK = new Object();
    private boolean mIsPause;

    /**
     * 該執行緒是否執行完畢
     */
    private boolean mIsFinish;

    /**
     * 設定未執行執行緒的不執行標識
     */
    private boolean mIsStop;

    /**
     * 檔案傳送監聽事件
     */
    private OnSendListener mOnSendListener;


    public FileSender(Socket socket, FileInfo fileInfo) {
        mSocket = socket;
        mFileInfo = fileInfo;
    }

    /**
     * 設定傳送監聽事件
     * @param onSendListener
     */
    public void setOnSendListener(OnSendListener onSendListener) {
        mOnSendListener = onSendListener;
    }

    @Override
    public void run() {
        if(mIsStop) {
            return;
        }

        //初始化
        try {
            if(mOnSendListener != null) {
                mOnSendListener.onStart();
            }
            init();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileSender init() ------->>> occur expection");
            if(mOnSendListener != null) {
                mOnSendListener.onFailure(e, mFileInfo);
            }
        }

        //傳送檔案實體資料
        try {
            parseBody();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileSender parseBody() ------->>> occur expection");
            if(mOnSendListener != null) {
                mOnSendListener.onFailure(e, mFileInfo);
            }
        }

        //檔案傳輸完畢
        try {
            finishTransfer();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.i("FileSender finishTransfer() ------->>> occur expection");
            if(mOnSendListener != null) {
                mOnSendListener.onFailure(e, mFileInfo);
            }
        }
    }

    @Override
    public void init() throws Exception {
        mSocket.setSoTimeout(30 * 1000);
        OutputStream os = mSocket.getOutputStream();
        mOutputStream = new BufferedOutputStream(os);
    }

    @Override
    public void parseBody() throws Exception {
        long fileSize = mFileInfo.getSize();
        File file = new File(mFileInfo.getFilePath());
        InputStream fis = new FileInputStream(file);

        int len = 0;
        long total = 0;
        byte[] bytes = new byte[BYTE_SIZE_DATA];

        long sTime = System.currentTimeMillis();
        long eTime = 0;
        while ((len = fis.read(bytes)) != -1) {
            synchronized (LOCK) {
                if(mIsPause) {
                    try {
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //寫入檔案
                mOutputStream.write(bytes, 0, len);
                total += len;

                //每隔200毫秒返回一次進度
                eTime = System.currentTimeMillis();
                if(eTime - sTime > 200) {
                    sTime = eTime;
                    if(mOnSendListener != null) {
                        mOnSendListener.onProgress(total, fileSize);
                    }
                }
            }
        }

        //關閉Socket輸入輸出流
        mOutputStream.flush();
        mOutputStream.close();
        //檔案傳送成功
        if(mOnSendListener != null) {
            mOnSendListener.onSuccess(mFileInfo);
        }
        mIsFinish = true;
    }

    @Override
    public void finishTransfer() throws Exception {
        if(mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {

            }
        }

        if(mSocket != null && mSocket.isConnected()) {
            try {
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 暫停傳送執行緒
     */
    public void pause() {
        synchronized (LOCK) {
            mIsPause = true;
            LOCK.notifyAll();
        }
    }

    /**
     * 恢復傳送執行緒
     */
    public void resume() {
        synchronized (LOCK) {
            mIsPause = false;
            LOCK.notifyAll();
        }
    }

    /**
     * 設定當前的傳送任務不執行
     */
    public void stop() {
        mIsStop = true;
    }

    /**
     * 檔案是否在傳送中
     * @return
     */
    public boolean isRunning() {
        return !mIsFinish;
    }

    public interface OnSendListener {
        void onStart();
        void onProgress(long progress, long total);
        void onSuccess(FileInfo fileInfo);
        void onFailure(Throwable throwable, FileInfo fileInfo);
    }
}

因為懶,以上列出的只是部分核心程式碼,選擇檔案的功能也沒去做,草草地在Activity中寫死了幾個檔案去上傳,具體程式碼可去Github下載執行,參見SendFilesActivity類,哈哈!

總結:

貌似第一次在部落格中貼那麼長的程式碼,關於Socket的知識還要學許多許多,而我懂的也只不過是入門的皮毛,以上Demo參考了以下大神博文及資料: